(转载)快速对图像的像素进行操作 opencv 实战

来源:互联网 发布:淘宝女装店知乎推荐 编辑:程序博客网 时间:2024/05/21 05:06

本文系转载,原文出处:http://blog.csdn.net/zwlq1314521/article/details/45038271

OpenCV 如何对图像的像素进行操作

对图像的像素进行操作,我们可以实现空间增强,反色,大部分图像特效系列都是基于像素操作等目的。先来看一下内存空间中图像矩阵,也就是Mat的矩阵数值部分是怎么存储的:

如果图像是一幅灰度图像,他就像这样,从左到右,从上到下,依次是矩阵的每一行每一列,这时候矩阵M(i,j)的值自然就是当前点的灰度值了。

而对于一幅彩色图像,由于它的像素分量channel并不是一个,所以每一列又分为了几个channel。拿常见的RGB图像来说,就像这样:

从这张图上,就可以比较清楚地看出来在内存中矩阵是如何存储多channel图像的了。这里要注意的是在RGB模型中,每一个子列依次为BGR,也就是正好是颠倒的,第一个分量是蓝色,第二个是绿色,第三个是红色。

清楚了图像在内存中的存储方式,我们也就可以来进行像素值的操作了。在这里,我们举这样一个例子。我们对一幅灰度图像的灰度值进行变换:

小于100的灰度值被统一映射为0;100到200之间的灰度值被映射为100;大于200的灰度值被映射为200.

主函数如下:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int main()  
  2. {  
  3.     string picName="lena.jpg";  
  4.     Mat A=imread (picName,CV_LOAD_IMAGE_GRAYSCALE);    //读入灰度图像  
  5.     uchar table[256];          //映射表,规定了变换前后灰度值的对应关系 table[gray_value_before]=gray_value_after  
  6.     for (int i=0;i<256;i++)  
  7.     {  
  8.         table[i]=i/100*100;   //这里利用了C++的语言特性i/100只会留下整数部分  
  9.     }  
  10.     imshow("变换前",A);  
  11.     Mat B=ChangeImg (A,table);    //变换函数  
  12.     imshow ("变换后",B);  
  13.     waitKey ();  
  14.     return 0;  
  15. }  

首先,我们用指针方式对图像的像素点灰度值进行操作:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. Mat  ChangeImg(Mat &img,const uchar* table)  
  2. {  
  3.     CV_Assert(img.depth ()!=sizeof(uchar));  //声明只对深度8bit的图像操作  
  4.     int channels=img.channels ();            //获取图像channel  
  5.     int nrows=img.rows;                     //矩阵的行数  
  6.     int ncols=img.cols*channels;             //矩阵的总列数=列数*channel分量数  
  7.     if (img.isContinuous ())               //判断矩阵是否连续,若连续,我们相当于只需要遍历一个一维数组  
  8.     {  
  9.         ncols*=nrows;  
  10.         nrows=1;                 //一维数组  
  11.     }  
  12.     //遍历像素点灰度值  
  13.     for (int i=0;i<nrows;i++)  
  14.     {  
  15.         uchar *p=img.ptr<uchar>(i);    //获取行地址  
  16.         for (int j=0;j<ncols;j++)  
  17.         {  
  18.             p[j]=table[p[j]];           //修改灰度值  
  19.         }  
  20.     }  
  21.     return img;  
  22. }  

这里,我们获取了每一行开始处的指针,然后遍历至该行末尾。如果矩阵是以连续方式存储的,我们只需请求一次指针、然后一路遍历下去就行。彩色图像的情况有必要加以注意:因为三个通道的原因,我们需要遍历的元素数目也是3倍。

或者,我们可以使用data。data会从Mat中返回指向矩阵第一行第一列的指针。注意如果该指针为NULL则表明对象里面无输入,所以这是一种简单的检查图像是否被成功读入的方法。当矩阵是连续存储时,我们就可以通过遍历 data 来扫描整个图像。例如,一个灰度图像,其操作如下:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. uchar* p = img.data;  
  2. for( unsigned int i =0; i < ncol*nrows; ++i)  
  3.     *p++ = table[*p];  

或者,更安全的方法,我们可以使用迭代器。在迭代法中,所需要做的仅仅是获得图像矩阵的begin和end,然后增加迭代直至从begin到end。将*操作符添加在迭代指针前,即可访问当前指向的内容。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)  
  2. {  
  3.     // accept only char type matrices  
  4.     CV_Assert(I.depth() != sizeof(uchar));       
  5.       
  6.     const int channels = I.channels();  
  7.     switch(channels)  
  8.     {  
  9.     case 1:   
  10.         {  
  11.             MatIterator_<uchar> it, end;   
  12.             for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)  
  13.                 *it = table[*it];  
  14.             break;  
  15.         }  
  16.     case 3:   
  17.         {  
  18.             MatIterator_<Vec3b> it, end;   
  19.             for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)  
  20.             {  
  21.                 (*it)[0] = table[(*it)[0]];  
  22.                 (*it)[1] = table[(*it)[1]];  
  23.                 (*it)[2] = table[(*it)[2]];  
  24.             }  
  25.         }  
  26.     }  
  27.       
  28.     return I;   
  29. }  

注意,在这里对3通道的图像进行操作的时候,使用到了Vec3b。Vec3b作为一个对三元向量的数据结构,用在这里正好是能够表示RGB的三个分量。如果对于彩色图像,仍然用uchar的话,则只能获得3通道中的B分量。比如我们可以这样打印出图像的RGB三个分量:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. for (int i=0;i<img.rows;i++)  
  2.     {  
  3.         const Vec3b* Mpoint=img.ptr <Vec3b>(i);  
  4.         for (int j=0;j<img.cols;j++)  
  5.         {  
  6.             Vec3b intensity=*(Mpoint+j);  
  7.             cout<<"R:"<<int(intensity[2])<<" G"<<int(intensity[1])<<" B"<<int(intensity[0])<<"    ";  
  8.         }  
  9.         cout<<endl;  
  10.     }  

这里使用指针,当然也可以使用上面的迭代器。

然而,OpenCV里面已经有了相应函数可以让我们更加方便地对像素进行操作,那便是LUT函数,而且推荐使用OpenCV的内建函数,因为已经针对芯片做了优化设计,使得速度有很大提升。

函数原型为:void LUT(InputArray src, InputArray lut, OutputArray dst, int interpolation=0 )

实现的映射关系如下所示:

也就是说比如原来src中值为1会映射为table[1]所对应的值再加上d。

所以上面的操作,我们其实只需要使用LUT函数就可以了。结合我们自己设计的table表,就能够实现对图像的操作。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. int main()  
  2. {  
  3.     string picName="lena.jpg";  
  4.     Mat A=imread (picName,CV_LOAD_IMAGE_GRAYSCALE);    //读入灰度图像  
  5.     Mat lookUpLut(1,256,CV_8UC1);   //建立一个256个元素的映射表  
  6.     imshow ("变换前",A);  
  7.     for (int i=0;i<256;i++)  
  8.     {  
  9.         lookUpLut.at<uchar>(i)=i/100*100;  
  10.     }  
  11.     Mat B;  
  12.     LUT (A,lookUpLut,B);  
  13.     imshow ("变换后",B);  
  14.     waitKey ();  
  15.     return 0;  
  16. }  

下面的图就是效果啦~~~

参考 http://www.xuebuyuan.com/1730735.html

补充:我们得出一些结论: 尽量使用 OpenCV 内置函数. 调用LUT 函数可以获得最快的速度. 这是因为OpenCV库可以通过英特尔线程架构启用多线程,下面的opencv矩阵操作均是优化的多线程并行处理,较高效



                                                                                        (复制不过来,直接参看原文连接吧)

OpenCV - Operations on Arrays 
对数组(矩阵)的一些操作Function (函数名)Use (函数用处)Author : Ggicci 
QQ : 771017478 / 854032390(prefer) 
转载请注明出处!

0 0
原创粉丝点击