OpenCV优化:图像的遍历4种方式

来源:互联网 发布:校园网络组建方案 编辑:程序博客网 时间:2024/05/18 08:00


OpenCV优化:图像的遍历4种方式

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

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

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

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

复制代码
 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 }
复制代码

image

通过上面的例子我们可以看出,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     // 把图像边缘像素设置为019     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 }
复制代码

image

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

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);

作者:☆Ronny丶

出处:http://www.cnblogs.com/ronny/

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 吃鸡界面有鼠标怎么办 老年机成英语了怎么办 手机成了英语了怎么办 塑料袋融化粘到衣服上怎么办 厕所被卫生纸堵了怎么办 钻石画的胶不粘了怎么办 客厅沙发选大了怎么办 连衣裙腰大了怎么办呀 憋尿后出现尿急尿涨怎么办 脚有酸酸的味道怎么办 三星手机home键失灵怎么办 三星s6返回键失灵怎么办 三星s7屏幕漏液怎么办 三星s8出现蓝框怎么办 三星手机短信图标没了怎么办 ps没有足够的ram怎么办 ps性能调不了是怎么办 苹果6p照相模糊怎么办 相框玻璃碎了怎么办 word文档加密后忘记密码怎么办 手机wps密码忘了怎么办 苹果手表忘了密码怎么办 苹果系统忘了密码怎么办 ps画板建小了怎么办 wps表格密码忘了怎么办 word文档变成虚的怎么办 wps论文中表格跨页怎么办 word文档复制过来有边框怎么办 wps表格跨页断开怎么办 锅的铆钉老是松怎么办 文胸不知道怎么染色了怎么办 未后的信息我该怎么办? 做leep手术后大出血怎么办 眼线笔出不了水怎么办 手机字体变成空心字怎么办 平安树树枝黑了怎么办 柳树被虫钻洞了怎么办 柳树叶子上有虫子怎么办 小金鱼翻肚皮了怎么办 秋天树叶没了小鸟怎么办 去国外旅游不会英语怎么办