遍历图像的4种方式

一、at<typename>(i,j)

Mat类提供了一个at的方法用于取得图像上的点,它是一个模板函数,可以取到任何类型的图像上的点。下面我们通过一个图像处理中的实际来说明它的用法。

在实际应用中,我们很多时候需要对图像降色彩,因为256*256*256实在太多了,在图像颜色聚类或彩色直方图时,我们需要用一些代表性的颜色代替丰富的色彩空间,我们的思路是将每个通道的256种颜色用64种代替,即将原来256种颜色划分64个颜色段,每个颜色段取中间的颜色值作为代表色。

void colorReduce(Mat& image,int div)
{
  for(int i=;i<image.rows;i++)
  {
    for(int j=;j<image.cols;j++)
    {
      image.at<Vec3b>(i,j)[]=image.at<Vec3b>(i,j)[]/div*div+div/;
      image.at<Vec3b>(i,j)[]=image.at<Vec3b>(i,j)[]/div*div+div/;
      image.at<Vec3b>(i,j)[]=image.at<Vec3b>(i,j)[]/div*div+div/;
    }
  }
}

通过上面的例子我们可以看出,at方法取图像中的点的用法:

image.at<uchar>(i,j):取出灰度图像中i行j列的点。

image.at<Vec3b>(i,j)[k]:取出彩色图像中i行j列第k通道的颜色点。其中uchar,Vec3b都是图像像素值的类型,不要对Vec3b这种类型感觉害怕,其实在core里它是通过typedef Vec<T,N>来定义的,N代表元素的个数,T代表类型。

更简单一些的方法:OpenCV定义了一个Mat的模板子类为Mat_,它重载了operator()让我们可以更方便的取图像上的点。

Mat_<uchar> im=image;

im(i,j)=im(i,j)/div*div+div/2;

二、高效一点:用指针来遍历图像

上面的例程中可以看到,我们实际喜欢把原图传进函数内,但是在函数内我们对原图像进行了修改,而将原图作为一个结果输出,很多时候我们需要保留原图,这样我们需要一个原图的副本。

void colorReduce(const Mat& image,Mat& outImage,int div)
{
// 创建与原图像等尺寸的图像
outImage.create(image.size(),image.type());
int nr=image.rows;
// 将3通道转换为1通道
int nl=image.cols*image.channels();
for(int k=;k<nr;k++)
{
// 每一行图像的指针
const uchar* inData=image.ptr<uchar>(k);
uchar* outData=outImage.ptr<uchar>(k);
for(int i=;i<nl;i++)
{
outData[i]=inData[i]/div*div+div/;
}
}
}

从上面的例子中可以看出,取出图像中第i行数据的指针:image.ptr<uchar>(i)。

值得说明的是:程序中将三通道的数据转换为1通道,在建立在每一行数据元素之间在内存里是连续存储的,每个像素三通道像素按顺序存储。也就是一幅图像数据最开始的三个值,是最左上角的那像素的三个通道的值。

但是这种用法不能用在行与行之间,因为图像在OpenCV里的存储机制问题,行与行之间可能有空白单元。这些空白单元对图像来说是没有意思的,只是为了在某些架构上能够更有效率,比如intel MMX可以更有效的处理那种个数是4或8倍数的行。但是我们可以申明一个连续的空间来存储图像,这个话题引入下面最为高效的遍历图像的机制。

三、更高效的方法

上面已经提到过了,一般来说图像行与行之间往往存储是不连续的,但是有些图像可以是连续的,Mat提供了一个检测图像是否连续的函数isContinuous()。当图像连通时,我们就可以把图像完全展开,看成是一行。

void colorReduce(const Mat& image,Mat& outImage,int div)
{
int nr=image.rows;
int nc=image.cols;
outImage.create(image.size(),image.type());
if(image.isContinuous()&&outImage.isContinuous())
{
nr=;
nc=nc*image.rows*image.channels();
}
for(int i=;i<nr;i++)
{
const uchar* inData=image.ptr<uchar>(i);
uchar* outData=outImage.ptr<uchar>(i);
for(int j=;j<nc;j++)
{
*outData++=*inData++/div*div+div/;
}
}
}

用指针除了用上面的方法外,还可以用指针来索引固定位置的像素:

image.step返回图像一行像素元素的个数(包括空白元素),image.elemSize()返回一个图像像素的大小。

image.at<uchar>(i,j)=image.data+i*image.step+j*image.elemSize();

四、还有吗?用迭代器来遍历。

下面的方法可以让我们来为图像中的像素声明一个迭代器:

MatIterator_<Vec3b> it;

Mat_<Vec3b>::iterator it;

如果迭代器指向一个const图像,则可以用下面的声明:

MatConstIterator<Vec3b> it; 或者

Mat_<Vec3b>::const_iterator it;

下面我们用迭代器来简化上面的colorReduce程序:

void colorReduce(const Mat& image,Mat& outImage,int div)
{
outImage.create(image.size(),image.type());
MatConstIterator_<Vec3b> it_in=image.begin<Vec3b>();
MatConstIterator_<Vec3b> itend_in=image.end<Vec3b>();
MatIterator_<Vec3b> it_out=outImage.begin<Vec3b>();
MatIterator_<Vec3b> itend_out=outImage.end<Vec3b>();
while(it_in!=itend_in)
{
(*it_out)[]=(*it_in)[]/div*div+div/;
(*it_out)[]=(*it_in)[]/div*div+div/;
(*it_out)[]=(*it_in)[]/div*div+div/;
it_in++;
it_out++;
}
}

如果你想从第二行开始,则可以从image.begin<Vec3b>()+image.rows开始。

上面4种方法中,第3种方法的效率最高!

opencv-01--图像的遍历的更多相关文章

  1. Opencv中图像的遍历与像素操作

    Opencv中图像的遍历与像素操作 OpenCV中表示图像的数据结构是cv::Mat,Mat对象本质上是一个由数值组成的矩阵.矩阵的每一个元素代表一个像素,对于灰度图像,像素是由8位无符号数来表示(0 ...

  2. 【OpenCV】图像的遍历

    Mat类的两种遍历比较快的方式,分别给出了按行和按列遍历,以及运行过程图. 原图: 按行遍历过程图 按列遍历过程图 代码如下: //ptr逐行访问 void ptrScanX(Mat& src ...

  3. Java基于opencv实现图像数字识别(五)—投影法分割字符

    Java基于opencv实现图像数字识别(五)-投影法分割字符 水平投影法 1.水平投影法就是先用一个数组统计出图像每行黑色像素点的个数(二值化的图像): 2.选出一个最优的阀值,根据比这个阀值大或小 ...

  4. Java基于opencv实现图像数字识别(四)—图像降噪

    Java基于opencv实现图像数字识别(四)-图像降噪 我们每一步的工作都是基于前一步的,我们先把我们前面的几个函数封装成一个工具类,以后我们所有的函数都基于这个工具类 这个工具类呢,就一个成员变量 ...

  5. Java基于opencv实现图像数字识别(三)—灰度化和二值化

    Java基于opencv实现图像数字识别(三)-灰度化和二值化 一.灰度化 灰度化:在RGB模型中,如果R=G=B时,则彩色表示灰度颜色,其中R=G=B的值叫灰度值:因此,灰度图像每个像素点只需一个字 ...

  6. Java基于opencv实现图像数字识别(二)—基本流程

    Java基于opencv实现图像数字识别(二)-基本流程 做一个项目之前呢,我们应该有一个总体把握,或者是进度条:来一步步的督促着我们来完成这个项目,在我们正式开始前呢,我们先讨论下流程. 我做的主要 ...

  7. 利用OpenCV给图像添加中文标注

    利用OpenCV给图像添加中文标注 : 参考:http://blog.sina.com.cn/s/blog_6bbd2dd101012dbh.html  和https://blog.csdn.net/ ...

  8. OpenCV中图像的格式Mat 图像深度

    opencv中图像的格式Mat 有图像的定义,图像深度.类型格式等,其中Mat的参数depth为深度,深度反应出图像颜色像素值: 关于数据的储存:(转) Mat_<uchar>对应的是CV ...

  9. Java基于opencv实现图像数字识别(一)

    Java基于opencv实现图像数字识别(一) 最近分到了一个任务,要做数字识别,我分配到的任务是把数字一个个的分开:当时一脸懵逼,直接百度java如何分割图片中的数字,然后就百度到了用Buffere ...

  10. OpenCV中图像算术操作与逻辑操作

    OpenCV中图像算术操作与逻辑操作 在图像处理中有两类最重要的基础操作各自是图像点操作与块操作.简单点说图像点操作就是图像每一个像素点的相关逻辑与几何运算.块操作最常见就是基于卷积算子的各种操作.实 ...

随机推荐

  1. OpenJudge计算概论-骑车与走路

    /*============================================================ 骑车与走路 总时间限制: 1000ms 内存限制: 65536kB 描述 ...

  2. android: Canvas的drawArc()方法的几个误区

    绘制圆环很多时候会用到Canvas的drawArc方法, drawArc()方法的说明很简单: public void drawArc (RectF oval, float startAngle, f ...

  3. LC 989. Add to Array-Form of Integer

    For a non-negative integer X, the array-form of X is an array of its digits in left to right order.  ...

  4. springboot之freemarker 和thymeleaf模板web开发

    Spring Boot 推荐使用Thymeleaf.FreeMarker.Velocity.Groovy.Mustache等模板引擎.不建议使用JSP. 一.Spring Boot 中使用Thymel ...

  5. build

    15:16:53: Running steps for project QQ_Client... 15:16:53: Starting: "/opt/Qt5.12.0/5.12.0/gcc_ ...

  6. PAT 甲级 1041 Be Unique (20 分)(简单,一遍过)

    1041 Be Unique (20 分)   Being unique is so important to people on Mars that even their lottery is de ...

  7. iOS-GCD处理后台线程和UI线程的交互

    一个例子: 在iPhone上做一个下载网页的功能,就是:在iPhone上放一个按钮,单击按钮时,显示一个转动的圆圈,表示正在进行下载,下载完成后,将内容加载到界面上的一个文本控件上. 使用GCD前: ...

  8. Oracle数据同步交换

    一.为了解决数据同步汇聚,数据分发,数据转换,数据维护等需求,TreeSoft将复杂的网状的同步链路变成了星型数据链路.     TreeSoft作为中间传输载体负责连接各种数据源,为各种异构数据库之 ...

  9. Flutter运行报错 `kernel_snapshot for errors` 解决方案

    Flutter运行报错 `flutter kernel_snapshot for errors`解决方案 当你Flutter项目删除了dart文件如果遇到 target:kernel_snapshot ...

  10. php 因循环数据 赋值变量 占用内存太大 提示错误

    Fatal error: Allowed memory size of 134217728 bytes exhausted 网上很多解决方法:就简单记录下 一个csv导入功能 由于数据太多 占用内存太 ...