转自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. iOS解析后台返回的二进制图片

    UIEdgeInsets insets = {0,0,0,0}; [self.showidentifyButton setImageEdgeInsets:insets]; NSData *imageD ...

  2. px和em,rem的区别

    任意浏览器的默认字体高都是16px. px: 像素(Pixel) , 计算机屏幕上的一个点.固定大小:不方便维护: em:相对于当前对象内 (父元素) 文本的字体尺寸.如当前对行内文本的字体尺寸未被人 ...

  3. PHP通过url下载远程图片到本地

    function GrabImage($url,$filename) { if($url==""):return false;endif; ob_start(); readfile ...

  4. 《JavaScript高级程序设计》读书笔记 ---变量、作用域和内存问题小结

    JavaScript 变量可以用来保存两种类型的值:基本类型值和引用类型值.基本类型的值源自以下5种基本数据类型:Undefined.Null.Boolean.Number 和String.基本类型值 ...

  5. jspl零散知识点

    1.  读取配置文件. index.jsp: <body> <% String charset=config.getInitParameter("charset" ...

  6. Java Web开发: Tomcat中部署项目的三种方法

    web开发,在tomcat中部署项目的方法: 可以参考http://m.blog.csdn.net/blog/u012516903/15741727 定义$CATALINA_HOME指的是Tomcat ...

  7. 查看使用了那种shell

    cat /etc/shells  root@OpenWrt:/www/cgi-bin# cat /etc/shells/bin/ash

  8. SharePoint 2013 APP 开发示例 (二)获取用户信息

    SharePoint 2013 APP 开发示例 (二)获取用户信息 这个示例里,我们将演示如何获取用户信息: 1. 打开 Visual Studio 2012. 2. 创建一个新的  SharePo ...

  9. flash跨域策略文件crossdomain.xml配置详解

    来源:http://www.2cto.com/Article/201108/100008.html 0x01 简介 flash在跨域时唯一的限制策略就是crossdomain.xml文件,该文件限制了 ...

  10. VS2012+EF6+Mysq

    为了学习ORM,选择了EntityFramework,经历了三天两夜的煎熬,N多次错误,在群里高手的帮助下,终于成功,现在将我的心路历程记录下来,一是让自己有个记录,另外就是让其它人少走些弯路. 我的 ...