遍历图像的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. app 爬虫

    https://mp.weixin.qq.com/s/ClYYfpvylQGlYYHDFBuKpA 唯品会舆情监控系统 姚彬炎 唯技术 2月26日  

  2. Tosca TestCases: Update all,Checkin all,Checkout,Checkout Tree

    #记录一下TestCases  Module 红框里面这几个button的功能 #Update all 从数据库里把别人最新checkin的东西给拽出来查看,也就是拿最新版本 #Checkin all ...

  3. phpstorm设置最多标签数

    在settings=>Editor=>General=>Editor Tabs的Tab limit下可以设置当前窗口能够打开的最多标签数目,默认是10,超过设定值时,会自动关闭前面的 ...

  4. keras 在train_on_batch中启用tensorboard

    def write_log(callback, names, logs, batch_no): for name, value in zip(names, logs): summary = tf.Su ...

  5. TensorFlow Object Detection API —— 测试自己的模型

    (flappbird) luo@luo-All-Series:~/MyFile/TensorflowProject/Mask_RCNN/mask_rcnn_20190518/Mask_RCNN/mod ...

  6. 利用Python获取cookie的方法,相比java代码简便不少

    1.通过urllib库,是python的标准库,不需要另外引入,直接看代码,注意代码的缩进: # coding=UTF-8import cookielibimport urllib2 class Ry ...

  7. jsPlumb 基本概念

    jsPlumb 基本概念 一.默认属性 Anchor:锚点(连接点位置),可以设置在任何没有锚点的目标上(endPoint) Anchors:设置在connect的源和目标点的连接点位置,默认是 Bo ...

  8. iOS笔试题02

    1. Difference between shallow copy and deep copy? 1> 浅拷贝:指针(地址)拷贝,不会产生新对象 2> 深拷贝:内容拷贝,会产生新对象 2 ...

  9. C#数字除法

    C#中计算double a=1/1000:应该结果是0.001,但为什么会变成0呢? C# 中 如果相除的两个数都是整数(int 型) 那么除的结果就是只取整数部分 所以你才会取到0 如果你要取精确的 ...

  10. 人工神经网络反向传播算法(BP算法)证明推导

    为了搞明白这个没少在网上搜,但是结果不尽人意,最后找到了一篇很好很详细的证明过程,摘抄整理为 latex 如下. (原文:https://blog.csdn.net/weixin_41718085/a ...