转自http://blog.csdn.net/daoqinglin/article/details/23628125

 for (int y = ; y < testImage->height; y++)
{ uchar * ptr = (uchar *)testImage->imageData + testImage->widthStep * y; for (int x = ; x < testImage->width; x++) { ptr[ * x + ] = ; ptr[ * x + ] = ; }
}

我们在实际应用中对图像进行的操作,往往并不是将图像作为一个整体进行操作,而是对图像中的所有点或特殊点进行运算,所以遍历图像就显得很重要,如何高效的遍历图像是一个很值得探讨的问题。

一、遍历图像的4种方式:at<typename>(i,j)

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

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

注意:image.at<Vec3b>(ROW,COL),如果对应某个点Point A,   则是image.at<Vec3b>(A.y,A.x)[0],顺序是反的。

 1 void colorReduce(Mat& image,int div)
2 {
3 for(int i=0;i<image.rows;i++)
4 {
5 for(int j=0;j<image.cols;j++)
6 {
7 image.at<Vec3b>(i,j)[0]=image.at<Vec3b>(i,j)[0]/div*div+div/2;
8 image.at<Vec3b>(i,j)[1]=image.at<Vec3b>(i,j)[1]/div*div+div/2;
9 image.at<Vec3b>(i,j)[2]=image.at<Vec3b>(i,j)[2]/div*div+div/2;
10 }
11 }
12 }

通过上面的例子我们可以看出,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;

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

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

 1 void colorReduce(const Mat& image,Mat& outImage,int div)
2 {
3 // 创建与原图像等尺寸的图像
4 outImage.create(image.size(),image.type());
5 int nr=image.rows;
6 // 将3通道转换为1通道
7 int nl=image.cols*image.channels();
8 for(int k=0;k<nr;k++)
9 {
10 // 每一行图像的指针
11 const uchar* inData=image.ptr<uchar>(k);
12 uchar* outData=outImage.ptr<uchar>(k);
13 for(int i=0;i<nl;i++)
14 {
15 outData[i]=inData[i]/div*div+div/2;
16 }
17 }
18 }

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

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

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

三、更高效的方法

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

 1 void colorReduce(const Mat& image,Mat& outImage,int div)
2 {
3 int nr=image.rows;
4 int nc=image.cols;
5 outImage.create(image.size(),image.type());
6 if(image.isContinuous()&&outImage.isContinuous())
7 {
8 nr=1;
9 nc=nc*image.rows*image.channels();
10 }
11 for(int i=0;i<nr;i++)
12 {
13 const uchar* inData=image.ptr<uchar>(i);
14 uchar* outData=outImage.ptr<uchar>(i);
15 for(int j=0;j<nc;j++)
16 {
17 *outData++=*inData++/div*div+div/2;
18 }
19 }
20 }

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

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程序:

 1 void colorReduce(const Mat& image,Mat& outImage,int div)
2 {
3 outImage.create(image.size(),image.type());
4 MatConstIterator_<Vec3b> it_in=image.begin<Vec3b>();
5 MatConstIterator_<Vec3b> itend_in=image.end<Vec3b>();
6 MatIterator_<Vec3b> it_out=outImage.begin<Vec3b>();
7 MatIterator_<Vec3b> itend_out=outImage.end<Vec3b>();
8 while(it_in!=itend_in)
9 {
10 (*it_out)[0]=(*it_in)[0]/div*div+div/2;
11 (*it_out)[1]=(*it_in)[1]/div*div+div/2;
12 (*it_out)[2]=(*it_in)[2]/div*div+div/2;
13 it_in++;
14 it_out++;
15 }
16 }

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

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

五、图像的邻域操作

很多时候,我们对图像处理时,要考虑它的邻域,比如3*3是我们常用的,这在图像滤波、去噪中最为常见,下面我们介绍如果在一次图像遍历过程中进行邻域的运算。

下面我们进行一个简单的滤波操作,滤波算子为[0 –1 0;-1 5 –1;0 –1 0]。

它可以让图像变得尖锐,而边缘更加突出。核心公式即:sharp(i.j)=5*image(i,j)-image(i-1,j)-image(i+1,j

)-image(i,j-1)-image(i,j+1)。

 1 void ImgFilter2d(const Mat &image,Mat& result)
2 {
3 result.create(image.size(),image.type());
4 int nr=image.rows;
5 int nc=image.cols*image.channels();
6 for(int i=1;i<nr-1;i++)
7 {
8 const uchar* up_line=image.ptr<uchar>(i-1);//指向上一行
9 const uchar* mid_line=image.ptr<uchar>(i);//当前行
10 const uchar* down_line=image.ptr<uchar>(i+1);//下一行
11 uchar* cur_line=result.ptr<uchar>(i);
12 for(int j=1;j<nc-1;j++)
13 {
14 cur_line[j]=saturate_cast<uchar>(5*mid_line[j]-mid_line[j-1]-mid_line[j+1]-
15 up_line[j]-down_line[j]);
16 }
17 }
18 // 把图像边缘像素设置为0
19 result.row(0).setTo(Scalar(0));
20 result.row(result.rows-1).setTo(Scalar(0));
21 result.col(0).setTo(Scalar(0));
22 result.col(result.cols-1).setTo(Scalar(0));
23 }

上面的程序有以下几点需要说明:

1,staturate_cast<typename>是一个类型转换函数,程序里是为了确保运算结果还在uchar范围内。

2,row和col方法返回图像中的某些行或列,返回值是一个Mat。

3,setTo方法将Mat对像中的点设置为一个值,Scalar(n)为一个灰度值,Scalar(a,b,c)为一个彩色值。

六、图像的算术运算

Mat类把很多算数操作符都进行了重载,让它们来符合矩阵的一些运算,如果+、-、点乘等。

下面我们来看看用位操作和基本算术运算来完成本文中的colorReduce程序,它更简单,更高效。

将256种灰度阶降到64位其实是抛弃了二进制最后面的4位,所以我们可以用位操作来做这一步处理。

首先我们计算2^8降到2^n中的n:int n=static_cast<int>(log(static_cast<double>(div))/log(2.0));

然后可以得到mask,mask=0xFF<<n;

用下面简直的语句就可以得到我们想要的结果:

result=(image&Scalar(mask,mask,mask))+Scalar(div/2,div/2,div/2);

很多时候我们需要对图像的一个通信单独进行操作,比如在HSV色彩模式下,我们就经常把3个通道分开考虑。

1 vector<Mat> planes;
2 // 将image分为三个通道图像存储在planes中
3 split(image,planes);
4 planes[0]+=image2;
5 // 将planes中三幅图像合为一个三通道图像
6 merge(planes,result);

OpenCV ——遍历图像方法的更多相关文章

  1. OpenCv遍历图像小结

    参考:http://www.cnblogs.com/ronny/p/opencv_road_2.html http://blog.csdn.net/xiaowei_cqu/article/detail ...

  2. OpenCV2+入门系列(三):遍历图像的几种方法

    根据OpenCV中Mat类型的结构和内存中存储方式,此处给出三种对图像进行遍历的方法.首先给出基础的读取图片代码,在中间替换三种遍历方法即可,本文中,程序将遍历图像并将所有像素点置为255,所有运行结 ...

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

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

  4. 11、OpenCV实现图像的灰度变换

    1.灰度变换的基本概念 灰度变换指对图像的单个像素进行操作,主要以对比度和阈值处理为目的.其变换形式如下: s=T(r) 其中,T是灰度变换函数:r是变换前的灰度:s是变换后的像素.图像灰度变换的有以 ...

  5. 12、OpenCV实现图像的直方图处理

    1.直方图 一幅图像由不同灰度值的像素组成,图像中灰度的分布情况是该图像的一个重要特征.图像的灰度直方图就描述了图像中灰度分布情况,能够很直观的展示出图像中各个灰度级所占的多少.图像的灰度直方图是灰度 ...

  6. 【图像处理】基于OpenCV实现图像直方图的原理

    背景 图像的直方图是衡量图像像素分布的一种方式,可以通过分析像素分布,使用直方图均衡化对图像进行优化,让图像变的清晰. opencv官方对图像直方图的定义如下: 直方图是图像中像素强度分布的图形表达方 ...

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

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

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

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

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

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

随机推荐

  1. 使用observable数组(Working with observable arrays)

    observable数组(observable arrays) 如果你要探测和响应一个对象的变化,你应该用observables.如果你需要探测和响应一个集合对象的变化,你应该用observableA ...

  2. 使用gulp构建nodejs,你只需要记住5个函数

    gulp gulp是一个nodejs的streaming构建工具,所谓的streaming大致意思就是把构建流程想成一个个链接的管道(pipe). 为什么要这样做呢? 要解释原因,就不得不提到unix ...

  3. C#中AES加密和解密

    /// AES加密 /// </summary> /// <param name="inputdata">输入的数据</param> /// & ...

  4. 转载 Deep learning:二(linear regression练习)

    前言 本文是多元线性回归的练习,这里练习的是最简单的二元线性回归,参考斯坦福大学的教学网http://openclassroom.stanford.edu/MainFolder/DocumentPag ...

  5. poll机制分析[转]

    所有的系统调用,基于都可以在它的名字前加上"sys_"前缀,这就是它在内核中对应的函数.比如系统调用open.read.write.poll,与之对应的内核函数为:sys_open ...

  6. 【转载】将绿色版Tomcat服务添加到系统服务并设为开机运行

    转自: http://www.cnblogs.com/lan0725/archive/2009/11/04/1873859.html   运行cmd打开控制台,进入Tomat目录/bin文件夹,输入如 ...

  7. 瑞游天翼客户端win7,win8,win10

    以管理员身份运行即可, 链接: http://pan.baidu.com/s/1kTw7VH9 密码: vttq

  8. android命令行网络时间同步

    一.简介 Android基于Linux平台的开源手机操作系统. 二.原理 既然是Linux,那就应该支持linux的各种命令行,高度的可配置,但实验发现Android是Google的一个高度阉割版的l ...

  9. mvc 母版页、用户自定义控件

    母版页(Master) 1.母版页是与Controller无关的,母版页只是一个View文件,而没有任何Controller与之相对应. 2.其实在ASP.NET MVC中View的aspx与母版页并 ...

  10. UIWebView是什么

    UIWebView类是用来显示网络内容.要使用它,可以简单的创造一个UIWebView对象,放置到窗口上,并且发送一个指向网络内容的请求.通过这个类,可以控制网页历史的前进後退,也可以通过程序去控制网 ...