2_opencv2计算机视觉学习_操作像素

  本次学习主要醋和操作图像的基本元素,如何遍历一张图像且处理其像素,并且在编程过程中,要考虑程序执行效率的问题!

        像素是由8位无符号数来表示,其中0表示黑色,255代表白色。对于彩色图来说,每个像素需要三个这样的8位无符号数来表示三个颜色通道(红绿蓝),所以矩阵的元素是一个三元数。保存不同像素类型有整形(CV_8U)浮点型(CV_32F)

1、存取像素值

         为了存取矩阵元素,需要访问矩阵的行和列。如果图像是单通道,返回值是单个值;如果图像是多通道,返回值是一组向量(Vector)。此例在图像中加入校验噪点,椒盐噪点就是随机的把部分像素设为白色或者黑色。在传输过程中,如果部分像素丢失,就会出现噪点。我们随机在图片中随机挑出若干像素,将其设置为白色。  

        首先我们创建一个salt函数,它的形参为一张图image和白噪点的个数n。我们用随机函数rand()结合图像的宽高随机选取一个点设置为噪点,用at访问该点,设置为白色。我们要设置n个噪点,那可以使用循环循环n次。另外,由于图像不知道是灰度图还是彩色图,我们要对其判断,程序如下:

[cpp]  view plain copy
  1. <span style="font-size:10px;"><span style="font-size:10px;">#include <iostream>  
  2. #include<opencv2/core/core.hpp>  
  3. #include<opencv2/highgui/highgui.hpp>  
  4. using namespace cv;  
  5. using namespace std;  
  6.   
  7. int main()  
  8. {  
  9.     void salt(Mat &image,int);//函数声明  
  10.        Mat image=imread("bridge.jpg");  
  11.     if(!image.data){  
  12.         cout<<"imread error";  
  13.         return 0;  
  14.     }  
  15.     namedWindow("bridge");  
  16.   //调用函数  
  17.     salt(image,3000);  
  18.     imshow("bridge",image);  
  19.     waitKey(0);  
  20.     return 1;  
  21. }  
  22. void salt(Mat &image,int n){//n为白噪点个数  
  23.     for(int k=0;k<n;k++){  
  24.         int j=rand()%image.rows;//行  
  25.         int i=rand()%image.cols;//列  
  26.         if(image.channels()==1){  
  27.             image.at<uchar>(j,i)=255;//at可以存取图像元素  
  28.         }else if(image.channels()==3){  
  29.             //设置三通道噪点颜色  
  30.             image.at<Vec3b>(j,i)[0]=255;  
  31.             image.at<Vec3b>(j,i)[1]=255;  
  32.             image.at<Vec3b>(j,i)[2]=255;  
  33.         }  
  34.     }  
  35. }  
  36. </span></span>  
        

注:

1、intj=rand()%image.rows;inti=rand()%image.cols;

由于随机数生成的数范围不确定,我们使用上述两条语句(取余)可以使得j,i不会超出image.rows;image.cols范围内

2、单通道元素访问image.at<uchar>(j,i)=255

多通道元素访问image.at<Vec3b>(j,i)[channel]=value;

3、cv::Mat_的使用重载操作符()

cv::Mat_<uchar>im2=image;//im2指向image

im2(50,100)=0;//存取50行100列元素

2、指针遍历图像

       在图像中,我们经常要遍历图像进行操作,考虑像素有可能非常多,所以高效的遍历图像是很必要的。首先使用的是指针算术。

       例子:减少图像中的颜色数目

       算法:对图像中像素每一个通道,将其值除以N(整数出发,舍去余数),在乘以N,得到不大于原始图像的N的最大倍数。对每个8位通道的值进行上述操作,可得到共计256/N*256/N*256/N个颜色值。程序如下:

方法一:指针运算

[cpp]  view plain copy
  1. void colorReduce1(Mat&image, int div=64){  
  2.     int nl=image.rows;//行数  
  3.     int nc=image.cols*image.channels();//列数:彩色图channel为3  
  4.     for(int j=0;j<nl;j++){  
  5.         //ptr访问j行地址  
  6.         uchar *data=image.ptr<uchar>(j);  
  7.         for(int i=0;i<nc;i++){  
  8.             data[i]=data[i]/div*div+div/2;//算法  
  9.             //*data++=*data/div*div+div/2;  
  10.             //data[i]=data[i]-data[i]%div+div/2 计算速度变慢存取每个像素两次  
  11.         }  
  12.     }  
  13. }  

方法二:位运算

[cpp]  view plain copy
  1. void colorReduce2(Mat&image, int div=64){  
  2.     int nl=image.rows;  
  3.     int nc=image.cols*image.channels();  
  4.     //位运算  
  5.     intn=static_cast<int>(log(static_cast<double>(div))/log(2.0));  
  6.     for(int j=0;j<nl;j++){  
  7.         uchar *data=image.ptr<uchar>(j);  
  8.         for(int i=0;i<nc;i++){  
  9.             //用来对像素取整的掩膜  
  10.              uchar mask=0xFF<<n;  
  11.              data[i]=(data[i]&mask)+div/2;  
  12.         }  
  13.     }  
  14. }  


方法三:高效遍历连续图像

         图像在行尾不进行填补时,图像可认为是一个长为W*H的一位数组,我们通过cv::Mat成员函数isContinue来判断,如果图像连续,整个处理使用一次循环完成。程序如下

[cpp]  view plain copy
  1. void colorReduce3(Mat&image, int div=64){  
  2.     int nl=image.rows;  
  3.     int nc=image.cols*image.channels();  
  4.     //判断图像是否连续  
  5.     if(image.isContinuous()){  
  6.         nc=nc*nl;  
  7.         nl=1;//一维数组  
  8.     }  
  9.     for(int j=0;j<nl;j++){  
  10.         uchar *data=image.ptr<uchar>(j);  
  11.         for(int i=0;i<nc;i++){  
  12. //处理每个像素  
  13.          data[i]=data[i]/div*div+div/2;  
  14.         }  
  15.     }  
  16. }  


方法四:底层指针运算 (不建议容易出错)

[cpp]  view plain copy
  1. void colorReduce4(Mat&image, int div=64){  
  2.     int nl=image.rows;  
  3.     int nc=image.cols*image.channels();  
  4.     //判断图像是否连续  
  5.     if(image.isContinuous()){  
  6.         nc=nc*nl;  
  7.         nl=1;//一维数组  
  8.     }  
  9.     for(int j=0;j<nl;j++){  
  10.         uchar *data=image.data;  
  11.        for(int i=0;i<nc;i++){  
  12.         data=image.data+i*image.step+i*image.elemSize();  
  13.        //data+=image.step;  
  14.         }  
  15.     }  
  16. }  


方法五:使用迭代器遍历图像

[cpp]  view plain copy
  1. void colorReduce5(Mat&image,int div=64){  
  2.     //迭代器的初始化  常量迭代器cv::Mat_<cv::Vec3b>::const_iterator it  
  3.     Mat_<Vec3b>::iteratorit=image.begin<Vec3b>();  
  4.     Mat_<Vec3b>::iteratoritend=image.end<Vec3b>();  
  5.     for(;it!=itend;it++){  
  6.         //三通道图像  
  7.         (*it)[0]=(*it)[0]/div*div+div/2;  
  8.         (*it)[1]=(*it)[1]/div*div+div/2;  
  9.         (*it)[2]=(*it)[2]/div*div+div/2;  
  10.     }  
  11. }  


方法六:at访问

[cpp]  view plain copy
  1. void colorReduce6(cv::Mat&image, int div=64) {  
  2.           int nl= image.rows;// number of lines  
  3.           int nc= image.cols;// number of columns  
  4.       for (int j=0; j<nl; j++) {  
  5.           for (int i=0; i<nc; i++) {  
  6.             // process each pixel---------------------  
  7. image.at<cv::Vec3b>(j,i)[0]=image.at<cv::Vec3b>(j,i)[0]/div*div+ div/2;  
  8. image.at<cv::Vec3b>(j,i)[1]=image.at<cv::Vec3b>(j,i)[1]/div*div+ div/2;  
  9. image.at<cv::Vec3b>(j,i)[2]=image.at<cv::Vec3b>(j,i)[2]/div*div + div/2;  
  10.             }              
  11.       }  
  12. }  
  13.    


方法七

[cpp]  view plain copy
  1. void colorReduce7(constcv::Mat &image, cv::Mat &result, int div=64) {  
  2.           int nl= image.rows;// number of lines  
  3.           int nc= image.cols ;// number of columns  
  4.           // allocate outputimage if necessary  
  5.          result.create(image.rows,image.cols,image.type());  
  6.           // created imageshave no padded pixels  
  7.           nc= nc*nl;  
  8.           nl= 1;  // it is now a 1D array  
  9.           int n=static_cast<int>(log(static_cast<double>(div))/log(2.0));  
  10.           // mask used to roundthe pixel value  
  11.           uchar mask=0xFF<<n; // e.g. for div=16, mask= 0xF0  
  12.           for (int j=0; j<nl; j++) {  
  13.                          uchar* data= result.ptr<uchar>(j);  
  14.                           constuchar* idata= image.ptr<uchar>(j);  
  15.           for (int i=0; i<nc; i++) {  
  16.             // process each pixel--------------------  
  17.             *data++= (*idata++)&mask + div/2;  
  18.             *data++= (*idata++)&mask +div/2;  
  19.             *data++= (*idata++)&mask +div/2;  
  20.           }                  
  21.       }  
  22. }  


结果如图,右图颜色明显减少

注:

迭代器是一种特殊的类,他专门用来遍历集合中的各个元素,同时隐藏了在给定集合上元素迭代的具体实现方式。

一个迭代器的声明如下:

cv::MatIterator_<cv::Vec3b> it;

另外一种使用定义在Mat_内部的迭代器类型

cv::Mat_<cv::Vec3b>::Iterator it;

这样就可以用常规的begin和end这两个迭代器方法遍历所以图像。如果你想从图像的第二行开始,可以用image.begin<cv::Vec3b>()+image.rows来初始化迭代器。

常量迭代器的声明:

cv::MatConstIterator_<cv::Vec3b>it;

cv::Mat_<cv::Vec3b>::const_iteratorit;

3、高效遍历循环

cv::getTickCount()

       测量一段代码的运行时间,这个函数返回从上次开始算起的时钟周期!我们测量的是代码运行的毫秒数,还需要另一个函数cv::getTickFrequency(),此函数返回每秒钟内的时钟周期数。统计代码运行时间如下:

[cpp]  view plain copy
  1. double duration;  
  2. duration=static_cast<double>(getTickCount());  
  3. colorReduce1(imageclone);  
  4. duration=(static_cast<double>(getTickCount())duration/getTickFrequency();  

前三小结总的程序如下

[cpp]  view plain copy
  1. #include<opencv2/core/core.hpp>  
  2. #include<iostream>  
  3. #include<opencv2/highgui/highgui.hpp>  
  4. usingnamespacecv;  
  5. usingnamespacestd;  
  6.    
  7. intmain()  
  8. {  
  9.     //函数声明  
  10.     voidcolorReduce1(Mat&image,intdiv=64);  
  11.     voidcolorReduce2(Mat&image,intdiv=64);  
  12.     voidcolorReduce3(Mat&image,intdiv=64);  
  13.     voidcolorReduce4(Mat&image,intdiv=64);  
  14.     voidcolorReduce5(Mat&image,intdiv=64);  
  15.     voidcolorReduce6(Mat&image,intdiv=64);  
  16.     voidcolorReduce7(Mat&image,intdiv=64);  
  17.    
  18.     Matimage=imread("bridge.jpg");  
  19.     //判断是否读入图片成功  
  20.     if(!image.data){  
  21.         cout<<"dataerror!";  
  22.     }  
  23.    
  24.     Matimageclone=image.clone();  
  25.     //定义一个数组存放记录不同程序运行时间  
  26.     doubleduration[8]={0};  
  27.     //6种方式运行时间程序  
  28.     duration[1]=static_cast<double>(getTickCount());  
  29.     colorReduce1(imageclone);  
  30.     duration[1]=(static_cast<double>(getTickCount())-duration[1])/getTickFrequency();  
  31.    
  32.     duration[2]=static_cast<double>(getTickCount());  
  33.     colorReduce2(imageclone);  
  34.     duration[2]=(static_cast<double>(getTickCount())-duration[2])/getTickFrequency();  
  35.    
  36.     duration[3]=static_cast<double>(getTickCount());  
  37.     colorReduce3(imageclone);  
  38.     duration[3]=(static_cast<double>(getTickCount())-duration[3])/getTickFrequency();  
  39.    
  40.     duration[4]=static_cast<double>(getTickCount());  
  41.     colorReduce4(imageclone);  
  42.     duration[4]=(static_cast<double>(getTickCount())-duration[4])/getTickFrequency();  
  43.    
  44.     duration[5]=static_cast<double>(getTickCount());  
  45.     colorReduce5(imageclone);  
  46.     duration[5]=(static_cast<double>(getTickCount())-duration[5])/getTickFrequency();  
  47.    
  48.     duration[6]=static_cast<double>(getTickCount());  
  49.     colorReduce6(imageclone);  
  50.     duration[6]=(static_cast<double>(getTickCount())-duration[6])/getTickFrequency();  
  51.    
  52.     duration[7]=static_cast<double>(getTickCount());  
  53.     colorReduce6(imageclone);  
  54.     duration[7]=(static_cast<double>(getTickCount())-duration[7])/getTickFrequency();  
  55.     //循环输出数组  
  56.     for(inti=1;i<=7;i++){  
  57.         cout<<"colorReduce"<<i<<"timeis:"<<duration[i]<<"(s)"<<endl;  
  58.     }  
  59.     namedWindow("Image");  
  60.     imshow("Image",image);  
  61.     namedWindow("result");  
  62.     imshow("result",imageclone);  
  63.     waitKey(0);  
  64.     return0;  
  65. }  
  66.    
  67.    
  68. //******************************************  
  69. voidcolorReduce1(Mat&image,intdiv=64){  
  70.     intnl=image.rows;//行数  
  71.     intnc=image.cols*image.channels();//列数:彩色图channel为3  
  72.     for(intj=0;j<nl;j++){  
  73.         //ptr访问j行地址  
  74.         uchar*data=image.ptr<uchar>(j);  
  75.         for(inti=0;i<nc;i++){  
  76.             data[i]=data[i]/div*div+div/2;//算法  
  77.             //*data++=*data/div*div+div/2;  
  78.             //data[i]=data[i]-data[i]%div+div/2计算速度变慢存取每个像素两次  
  79.         }  
  80.     }  
  81. }  
  82.    
  83. //*****************************************  
  84. voidcolorReduce2(Mat&image,intdiv=64){  
  85.     intnl=image.rows;  
  86.     intnc=image.cols*image.channels();  
  87.     //位运算  
  88.     intn=static_cast<int>(log(static_cast<double>(div))/log(2.0));  
  89.     for(intj=0;j<nl;j++){  
  90.         uchar*data=image.ptr<uchar>(j);  
  91.         for(inti=0;i<nc;i++){  
  92.             //用来对像素取整的掩膜  
  93.              ucharmask=0xFF<<n;  
  94.              data[i]=(data[i]&mask)+div/2;  
  95.         }  
  96.     }  
  97. }  
  98.    
  99. //***********************************  
  100. voidcolorReduce3(Mat&image,intdiv=64){  
  101.     intnl=image.rows;  
  102.     intnc=image.cols*image.channels();  
  103.     //判断图像是否连续  
  104.     if(image.isContinuous()){  
  105.         nc=nc*nl;  
  106.         nl=1;//一维数组  
  107.     }  
  108.     for(intj=0;j<nl;j++){  
  109.         uchar*data=image.ptr<uchar>(j);  
  110.         for(inti=0;i<nc;i++){  
  111. //处理每个像素  
  112.          data[i]=data[i]/div*div+div/2;  
  113.         }  
  114.     }  
  115. }  
  116.    
  117. //********************************************  
  118. voidcolorReduce4(Mat&image,intdiv=64){  
  119.     intnl=image.rows;  
  120.     intnc=image.cols*image.channels();  
  121.     //判断图像是否连续  
  122.     if(image.isContinuous()){  
  123.         nc=nc*nl;  
  124.         nl=1;//一维数组  
  125.     }  
  126.     for(intj=0;j<nl;j++){  
  127.         uchar*data=image.data;  
  128.         for(inti=0;i<nc;i++){  
  129.          data=image.data+i*image.step+i*image.elemSize();  
  130.        //data+=image.step;  
  131.         }  
  132.     }  
  133. }  
  134.    
  135. //********************************************  
  136. voidcolorReduce5(Mat&image,intdiv=64){  
  137.     //迭代器的初始化 常量迭代器cv::Mat_<cv::Vec3b>::const_iteratorit  
  138.     Mat_<Vec3b>::iteratorit=image.begin<Vec3b>();  
  139.     Mat_<Vec3b>::iteratoritend=image.end<Vec3b>();  
  140.     for(;it!=itend;it++){  
  141.         //三通道图像  
  142.         (*it)[0]=(*it)[0]/div*div+div/2;  
  143.         (*it)[1]=(*it)[1]/div*div+div/2;  
  144.         (*it)[2]=(*it)[2]/div*div+div/2;  
  145.     }  
  146. }  
  147.    
  148. //********************************************  
  149. voidcolorReduce6(cv::Mat&image,intdiv=64){  
  150.       intnl=image.rows;//numberoflines  
  151.       intnc=image.cols;//numberofcolumns  
  152.       for(intj=0;j<nl;j++){  
  153.           for(inti=0;i<nc;i++){  
  154.             //processeachpixel---------------------  
  155. image.at<cv::Vec3b>(j,i)[0]=image.at<cv::Vec3b>(j,i)[0]/div*div+div/2;  
  156. image.at<cv::Vec3b>(j,i)[1]=image.at<cv::Vec3b>(j,i)[1]/div*div+div/2;  
  157. image.at<cv::Vec3b>(j,i)[2]=image.at<cv::Vec3b>(j,i)[2]/div*div+div/2;  
  158.             }  
  159.       }  
  160. }  
  161. //********************************************  
  162. voidcolorReduce7(constcv::Mat&image,cv::Mat&result,intdiv=64){  
  163.       intnl=image.rows;//numberoflines  
  164.       intnc=image.cols;//numberofcolumns  
  165.       //allocateoutputimageifnecessary  
  166.       if(image.isContinuous()){  
  167.           //createdimageshavenopaddedpixels  
  168.           nc=nc*nl;  
  169.           nl=1; //itisnowa1Darray  
  170.       }  
  171.       intn=static_cast<int>(log(static_cast<double>(div))/log(2.0));  
  172.       //maskusedtoroundthepixelvalue  
  173.       ucharmask=0xFF<<n;//e.g.fordiv=16,mask=0xF0  
  174.           for(intj=0;j<nl;j++){  
  175.           uchar*data=result.ptr<uchar>(j);  
  176.           constuchar*idata=image.ptr<uchar>(j);  
  177.           for(inti=0;i<nc;i++){  
  178.             //processeachpixel--------------------  
  179.             *data++=(*idata++)&mask+div/2;  
  180.             *data++=(*idata++)&mask+div/2;  
  181.             *data++=(*idata++)&mask+div/2;  
  182.           }  
  183.       }  
  184. }  

4、遍历图像和邻域操作

        图像处理中,当邻域包含图像的前几行和下几行时,需要同时扫描图像的若干行!

       例子:对图像进行锐化

       它基于拉普拉斯算子,将一幅图像减去他经过拉普拉斯滤波后的图像,这幅图的边缘部分将得到放大,即细节更加锐利。图像遍历使用三个指针:一个指向当前行,一个指向上一行,一个下一行。由于每个像素值的计算都需要它的上下左右四个邻居像素,所以不可能对图像的第一行、最后一行、第一列、最后一列进行计算。锐化算子的计算方式如下:

sharpened_pixel=5*current-left-right-up-dowm;

程序如下:

[cpp]  view plain copy
  1. <span style="font-weight: normal;">#include<iostream>  
  2. #include<opencv2/core/core.hpp>  
  3. #include<opencv2/highgui/highgui.hpp>  
  4. #include<opencv2/imgproc/imgproc.hpp>  
  5.    
  6. voidsharpen(constcv::Mat&image,cv::Mat&result){  
  7.    
  8.     result.create(image.size(),image.type());//分配大小  
  9.    
  10.     for(intj=1;j<image.rows-1;j++){//除了第一行和最后一行进行遍历  
  11.    
  12.         constuchar*previous=image.ptr<constuchar>(j-1);//上一行  
  13.         constuchar*current=image.ptr<constuchar>(j);         //当前行  
  14.         constuchar*next=image.ptr<constuchar>(j+1);                         //下一行  
  15.    
  16.         uchar*output=result.ptr<uchar>(j);  
  17.    
  18.         for(inti=1;i<image.cols-1;i++){  
  19.    
  20.             *output++=cv::saturate_cast<uchar>(5*current[i]-current[i-1]-current[i+1]-previous[i]-next[i]);  
  21. //                                          output[i]=cv::saturate_cast<uchar>(5*current[i]-current[i-1]-current[i+1]-previous[i]-next[i]);  
  22.         }  
  23.     }  
  24.    
  25.     //未处理像素设置为0  
  26.     result.row(0).setTo(cv::Scalar(0));  
  27.     result.row(result.rows-1).setTo(cv::Scalar(0));  
  28.     result.col(0).setTo(cv::Scalar(0));  
  29.     result.col(result.cols-1).setTo(cv::Scalar(0));  
  30. }  
  31.    
  32. voidsharpen2(constcv::Mat&image,cv::Mat&result){  
  33.    
  34.     result.create(image.size(),image.type());//allocateifnecessary  
  35.    
  36.     intstep=image.step1();  
  37.     constuchar*previous=image.data;  
  38.     constuchar*current= image.data+step;  
  39.     constuchar*next=image.data+2*step;  
  40.     uchar*output=result.data+step;  
  41.     for(intj=1;j<image.rows-1;j++){//foreachrow(exceptfirstandlast)  
  42.         for(inti=1;i<image.cols-1;i++){//foreachcolumn(exceptfirstandlast)  
  43.    
  44.             output[i]=cv::saturate_cast<uchar>(5*current[i]-current[i-1]-current[i+1]-previous[i]-next[i]);  
  45.         }  
  46.    
  47.         previous+=step;  
  48.         current+=step;  
  49.         next+=step;  
  50.         output+=step;  
  51.     }  
  52.    
  53.     result.row(0).setTo(cv::Scalar(0));  
  54.     result.row(result.rows-1).setTo(cv::Scalar(0));  
  55.     result.col(0).setTo(cv::Scalar(0));  
  56.     result.col(result.cols-1).setTo(cv::Scalar(0));  
  57. }  
  58.    
  59. voidsharpen3(constcv::Mat&image,cv::Mat&result){  
  60.    
  61.     cv::Mat_<uchar>::const_iteratorit=image.begin<uchar>()+image.step;  
  62.     cv::Mat_<uchar>::const_iteratoritend=image.end<uchar>()-image.step;  
  63.     cv::Mat_<uchar>::const_iteratoritup=image.begin<uchar>();  
  64.     cv::Mat_<uchar>::const_iteratoritdown=image.begin<uchar>()+2*image.step;  
  65.    
  66.     result.create(image.size(),image.type());  
  67.     cv::Mat_<uchar>::iteratoritout=result.begin<uchar>()+result.step;  
  68.    
  69.     for(;it!=itend;++it,++itup,++itdown){  
  70.    
  71.             *itout=cv::saturate_cast<uchar>(*it*5-*(it-1)-*(it+1)-*itup-*itdown);  
  72.     }  
  73. }  
  74.    
  75. intmain()  
  76. {  
  77.     cv::Matimage=cv::imread("bridge.jpg",0);  
  78.     if(!image.data)  
  79.         return0;  
  80.    
  81.     cv::Matresult;  
  82.     result.create(image.size(),image.type());  
  83.    
  84.     doubletime=static_cast<double>(cv::getTickCount());  
  85.     sharpen(image,result);  
  86.     time=(static_cast<double>(cv::getTickCount())-time)/cv::getTickFrequency();  
  87.     std::cout<<"time="<<time<<std::endl;  
  88.     cv::namedWindow("Image");  
  89.     cv::imshow("Image",result);  
  90.    
  91.     image=cv::imread("bridge.jpg",0);  
  92.     time=static_cast<double>(cv::getTickCount());  
  93.     sharpen3(image,result);  
  94.     time=(static_cast<double>(cv::getTickCount())-time)/cv::getTickFrequency();  
  95.     std::cout<<"time3="<<time<<std::endl;  
  96.    
  97.     cv::namedWindow("Image3");  
  98.     cv::imshow("Image3",result);  
  99.     cv::waitKey();  
  100.     return0;  
  101. }</span>  
  102.    


5、简单图像算术

图像就是一些矩阵,我们对矩阵进行运算从而就改变了图像的性质。在此次学习中,我们对两幅图进行相加,可以分为两种情况:

①相加的的两张图为类型和大小相同的

②相加的的两张图不一样

类型和大小相同的两张图如下:

调用函数

cv::addWeighted()用法如下:(具体含义见Reference Manual)

void addWeighted(InputArray src1, double alpha,InputArray src2, double beta, double gamma, OutputArray dst, int dtype=-1)

可以使用dst = src1*alpha + src2*beta + gamma;

例如c[i]=a[i]+b[i]等价于cv::add(imageA,imageB,resultC)

c[i]=a[i]+k等价于cv::add(imageA,cv::Scalar(k),resultC)

c[i]=k1*a[i]+k2*b[i]+k3

等价于cv::addWeighted(imageA,k1,imageB,k2,k3,imageC)

我们也可以重载操作符:

Result=0.7*imageA+0.9*imageB+0.8;

此次程序为:

[cpp]  view plain copy
  1. #include<iostream>  
  2. #include<opencv2/core/core.hpp>  
  3. #include<opencv2/highgui/highgui.hpp>  
  4. usingnamespacecv;  
  5. usingnamespacestd;  
  6.    
  7. intmain(){  
  8.     //读取相加的两张图片  
  9.     cv::Matimage1=imread("bridge.jpg");;  
  10.     cv::Matimage2=imread("sun.jpg");;  
  11.     //判断是否读取成功  
  12.     if(!image1.data)  
  13.         return0;  
  14.     if(!image2.data)  
  15.         return0;  
  16.     //显示两张图  
  17.     cv::namedWindow("Image1");  
  18.     cv::imshow("Image1",image1);  
  19.     cv::namedWindow("Image2");  
  20.     cv::imshow("Image2",image2);  
  21. //方法一  
  22.     cv::Matresult;  
  23.     //addweighted两张图需要大小类型相同  
  24.     cv::addWeighted(image1,0.7,image2,0.9,0.,result);  
  25.     //显示相加的结果  
  26.     cv::namedWindow("result");  
  27.     cv::imshow("result",result);  
  28. //方法二  
  29.     //重载运算符“+”,“*”  
  30.     result=0.7*image1+0.9*image2;  
  31.     //显示  
  32.     cv::namedWindow("resultwithoperators");  
  33.     cv::imshow("resultwithoperators",result);  
  34.     image2=cv::imread("rain.jpg",0);  
  35.     cv::waitKey();  
  36.     return0;  
  37.     }  


叠加结果为:


类型不同的两张图

Logo.bmp           bridge.jpg


我们要把大小和类型不同两张图加到一起,由于cv::add要求输出图像要相同的尺寸,所以不能用。我们首先要定义感兴趣区域(ROI),只要感兴趣区域和logo大小相同,cv::add就能工作了!

//定义感兴趣区域

        cv::MatimageROI;

        imageROI=image(cv::Rect(300,300,logo.cols,logo.rows));

        //插入logo

        cv::addWeighted(imageROI,1.0,logo,0.5,0.,imageROI);

直接相加得到图像


得到的图像不是很令人满意,可以将插入处的像素设置为logo图像的像素效果会更好,可以通过一个图像掩码完成:

imageROI= image(cv::Rect(300,300,logo.cols,logo.rows));

        // 加载掩膜(必须为灰度图)

        cv::Matmask= cv::imread("logo.bmp",0);

        // 拷贝拷贝ROI

        logo.copyTo(imageROI,mask);

得到


程序为

[cpp]  view plain copy
  1. <span style="font-size:10px;">#include<iostream>  
  2. #include<opencv2/core/core.hpp>  
  3. #include<opencv2/highgui/highgui.hpp>  
  4. usingnamespacecv;  
  5. usingnamespacestd;  
  6.    
  7. intmain(){  
  8.    
  9.     //感兴趣区域  
  10.         cv::Matimage=cv::imread("bridge.jpg");  
  11.         cv::Matlogo=cv::imread("logo.bmp");  
  12.         //定义感兴趣区域  
  13.         cv::MatimageROI;  
  14.         imageROI=image(cv::Rect(300,300,logo.cols,logo.rows));  
  15.         //插入logo  
  16.         cv::addWeighted(imageROI,1.0,logo,0.5,0.,imageROI);  
  17.         cv::namedWindow("withlogo");  
  18.         cv::imshow("withlogo",image);  
  19.     //用掩膜  
  20.         logo=cv::imread("logo.bmp");  
  21.         imageROI=image(cv::Rect(300,300,logo.cols,logo.rows));  
  22.     //加载掩膜(必须为灰度图)  
  23.         cv::Matmask=cv::imread("logo.bmp",0);  
  24.         //拷贝拷贝ROI  
  25.         logo.copyTo(imageROI,mask);  
  26.         cv::namedWindow("withlogo2");  
  27.         cv::imshow("withlogo2",image);  
  28.    
  29.         logo=cv::imread("logo.bmp",0);  
  30.         image=cv::imread("bridge.jpg");  
  31.    
  32.     cv::waitKey();  
  33.     return0;  
  34.     }  
  35.  </span>  


网址:http://blog.csdn.net/yale524/article/details/44837465
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值