访问图像像素
存储方式
BGR连续存储有助于提升图像扫描速度。
isContinuous()判断是否是连续存储。
颜色空间缩减
仅用这些颜色中具有代表性的很小的部分,就足以达到同样的效果。
将现有颜色空间值除以某个输入值,获得较少的颜色数。
LUT函数:look up table操作
用于批量进行图像元素查找、扫描和操作图像。
使用方法如下:
1 Mat lookUpTable(1,256,CV_8U); 2 uchar* p = lookUpTable.data; 3 for(int i=0;i<256;i++) 4 p[i] = table[i]; 5 //然后调用函数(I是输入,J是输出) 6 for(int i=0;i<times;++i) 7 LUT(I,lookUpTable,J);
计时函数
两个函数组合起来使用:
double time0 = static_cast<double>(getTickCount());//记录起始时间 //进行图像处理。。。。。。 time0 = ((double)getTickCount() - time0 ) / getTickFrequency(); cout << "此方法运行时间" << time0 << "秒" << endl;//输出运行时间
访问图像中像素的三类方法
要求:减少图像中颜色的数量,比如原来的图像是256种颜色,希望它变成64种颜色,只需将原来颜色除以4(整除)以后再乘以4就可以
- 方法一,指针访问:C操作符[];
- 方法二,迭代器iterator
- 方法三,动态地址计算
方法一
1 //---------------------------------【头文件、命名空间包含部分】-------------------------- 2 // 描述:包含程序所使用的头文件和命名空间 3 //----------------------------------------------------------------------------------------------- 4 #include <opencv2/core/core.hpp> 5 #include <opencv2/highgui/highgui.hpp> 6 #include <iostream> 7 using namespace std; 8 using namespace cv; 9 10 //-----------------------------------【全局函数声明部分】----------------------------------- 11 // 描述:全局函数声明 12 //----------------------------------------------------------------------------------------------- 13 void colorReduce(Mat& inputImage, Mat& outputImage, int div); 14 15 //--------------------------------------【main( )函数】--------------------------------------- 16 // 描述:控制台应用程序的入口函数,我们的程序从这里开始执行 17 //----------------------------------------------------------------------------------------------- 18 int main( ) 19 { 20 //【1】创建原始图并显示 21 Mat srcImage = imread("1.jpg"); 22 imshow("原始图像",srcImage); 23 24 //【2】按原始图的参数规格来创建创建效果图 25 Mat dstImage; 26 dstImage.create(srcImage.rows,srcImage.cols,srcImage.type());//效果图的大小、类型与原图片相同 27 28 //【3】记录起始时间 29 double time0 = static_cast<double>(getTickCount()); 30 printf(" 当前使用的OpenCV版本为:" CV_VERSION ); 31 32 //【4】调用颜色空间缩减函数 33 colorReduce(srcImage,dstImage,32); 34 35 //【5】计算运行时间并输出 36 time0 = ((double)getTickCount() - time0)/getTickFrequency(); 37 cout<<" 此方法运行时间为: "<<time0<<"秒"<<endl; //输出运行时间 38 39 //【6】显示效果图 40 imshow("效果图",dstImage); 41 waitKey(0); 42 } 43 44 45 //---------------------------------【colorReduce( )函数】--------------------------------- 46 // 描述:使用【指针访问:C操作符[ ]】方法版的颜色空间缩减函数 47 //---------------------------------------------------------------------------------------------- 48 void colorReduce(Mat& inputImage, Mat& outputImage, int div) //div是要转成多少种颜色的数量 49 { 50 //参数准备 51 outputImage = inputImage.clone(); //拷贝实参到临时变量 52 int rowNumber = outputImage.rows; //行数 53 int colNumber = outputImage.cols * outputImage.channels(); //列数 x 通道数=每一行元素的个数 54 55 //双重循环,遍历所有的像素值 56 for(int i = 0;i < rowNumber;i++) //行循环 57 { 58 uchar* data = outputImage.ptr<uchar>(i); //获取第i行的首地址 59 for(int j = 0;j < colNumber;j++) //列循环 60 { 61 // ---------【开始处理每个像素】------------- 62 data[j] = data[j]/div*div + div/2; 63 //可以等效使用指针运算从一列移动到下一列,所以还可以这样写: 64 // *data++ = *data/div*div + div/2; 65 // ----------【处理结束】--------------------- 66 } //行处理结束 67 } 68 }
Mat类有若干成员函数可以获取图像的属性。公有成员变量cols和rows给出了图像的宽和高,成员函数channels()用于返回图像的通道数。
ptr函数可以得到图像任意行的首地址,ptr是模板函数,返回第i行的首地址。
方法二:
获得图像矩阵的begin和end,然后增加迭代从begin到end,将*操作符添加在迭代指针前,即可访问当前指向的内容。
相比用指针直接访问可能出现越界问题,迭代器很安全。
1 //-------------------------------------【colorReduce( )函数】----------------------------- 2 // 描述:使用【迭代器】方法版的颜色空间缩减函数 3 //---------------------------------------------------------------------------------------------- 4 void colorReduce(Mat& inputImage, Mat& outputImage, int div) 5 { 6 //参数准备 7 outputImage = inputImage.clone(); //拷贝实参到临时变量 8 //获取迭代器 9 Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>(); //初始位置的迭代器 10 Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>(); //终止位置的迭代器 11 12 //存取彩色图像像素 13 for(;it != itend;++it) 14 { 15 // ------------------------【开始处理每个像素】-------------------- 16 (*it)[0] = (*it)[0]/div*div + div/2; 17 (*it)[1] = (*it)[1]/div*div + div/2; 18 (*it)[2] = (*it)[2]/div*div + div/2; //三个通道 19 // ------------------------【处理结束】---------------------------- 20 } 21 }
方法三:
1 //----------------------------------【colorReduce( )函数】------------------------------- 2 // 描述:使用【动态地址运算配合at】方法版本的颜色空间缩减函数 3 //---------------------------------------------------------------------------------------------- 4 void colorReduce(Mat& inputImage, Mat& outputImage, int div) 5 { 6 //参数准备 7 outputImage = inputImage.clone(); //拷贝实参到临时变量 8 int rowNumber = outputImage.rows; //行数 9 int colNumber = outputImage.cols; //列数 10 11 //存取彩色图像像素 12 for(int i = 0;i < rowNumber;i++) 13 { 14 for(int j = 0;j < colNumber;j++) 15 { 16 // ------------------------【开始处理每个像素】-------------------- 17 outputImage.at<Vec3b>(i,j)[0] = outputImage.at<Vec3b>(i,j)[0]/div*div + div/2; //蓝色通道 18 outputImage.at<Vec3b>(i,j)[1] = outputImage.at<Vec3b>(i,j)[1]/div*div + div/2; //绿色通道 19 outputImage.at<Vec3b>(i,j)[2] = outputImage.at<Vec3b>(i,j)[2]/div*div + div/2; //红是通道 20 // -------------------------【处理结束】---------------------------- 21 } // 行处理结束 22 } 23 }
Mat类的成员函数at(int x,int y)可以用来存取图像元素。要确保指定的数据类型要和矩阵中的数据类型相符合,因为at方法本身不会对任何数据类型进行转换。
对于一个包含彩色图像的Mat,会返回一个有三个8位数组成的向量。opencv将此类型的向量定义为Vec3b,即有三个unsigned char组成的向量,这也解释了为什么存取彩色图像像素的代码的形式:
1 image.at<Vec3b>(j,i)[channel]=value;
感兴趣区域:ROI
这个区域是图像分析所关注的重点,圈住这个区域,方便进行进一步的处理。使用ROI指定想读入的目标,可以减少处理时间,提升精度,带来很大便利。
定义ROI区域的方法有两种:
第一种是使用表示矩形区域的Rect。指定矩形的左上角坐标和矩形的weidth和heigth
1 Mat imageROI; 2 imageROI = image(Rect(500,250,logo.cols,logo.rows))第二种指定ROI的行或列的范围Range。Range是指起始索引到终止索引(不包括终止索引)的一段连续序列:
1 imageROI = image(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));
下面代码中,通过一个图像掩模,直接将插入处的像素设置为logo图像的像素值,这样效果会很逼真。
1 //-----ROI_AddImage()函数 2 //利用感兴趣区域ROI实现图像叠加 3 bool ROI_AddImage() 4 { 5 //1、读入图像 6 Mat srcImagel = imread("doata"); 7 Mat logoImage = imread("dota_logo.jpg"); 8 if(!srcImagel.data) 9 { 10 printf("读取错误! "); 11 return false; 12 } 13 if(!logoImage.data) 14 { 15 printf("读取错误! "); 16 return false; 17 } 18 19 //2、定义一个Mat类型并给其设定ROI区域 20 Mat imageROI = srcImagel(Rect(200,250,logoImage.cols,logoImage.rows)); 21 //3、加载掩模(必须是灰度图) 22 Mat mask = imread("dota_logo.jpg",0); 23 //4、将掩模复制到ROI 24 logoImage.copyTo(imageROI,mask); 25 //5、显示结果 26 namedWindow("test"); 27 imshow("test",srcImagel); 28 return true; 29 }
图像掩模一般用来对处理的图像(全部或者局部)进行遮挡,来控制图像处理的区域或处理过程。
掩模一般是小于等于源图像的单通道矩阵,掩模中的值分为两种0和非0。以Mat::copyTo为例,当mask的值不为0,则将源图像拷贝到目标图像,当mask为0,则不进行拷贝,目标图像保持不变。
线性混合操作
线性混合操作是一种典型的二元(两个输入)的像素操作,它的理论公式如下:
通过范围0~1之间改变alpha值,来对两幅图像f(x)或两段视频产生时间上的画面叠化效果,就想幻灯片放映和电影制作中的那样,也就是在幻灯片翻页时设置的前后叶缓慢过度叠加效果。
主要用到了addWeighted函数。
计算数组加权和:addWeighted函数
这个函数的作用是计算两个数组(图像阵列)的加权和:
void addWeighted(inputarray src1, double alpha, inputarray arc2, double beta, double gamma, outputarray dst, int dtyoe=-1);
(1)需要加权的第一个数组,常常填一个Mat
(2)alpht,第一个数组的权重
(3)src2,第二个数组,和第一个数组拥有相同的尺寸和通道数
(4)beta,第二个数组的权重值
(5)gamma,一个加到权重总和上的标量值
(6)dst,输出的数组,和输入的两个数组有相同的尺寸和通道数
(7)dtype,输出阵列的可选深度,默认-1,。当两个输入数组具有相同的深度时,这个参数设置为-1。
最终的计算结果:
其中I是多维数组的索引值。每个通道都要独立的进行处理。当输出数组的深度为CV_32S时,这个函数就不适用了。
1 /---------------------------------【LinearBlending()函数】------------------------------------- 2 // 函数名:LinearBlending() 3 // 描述:利用cv::addWeighted()函数实现图像线性混合 4 //-------------------------------------------------------------------------------------------- 5 bool LinearBlending() 6 { 7 //【0】定义一些局部变量 8 double alphaValue = 0.5; 9 double betaValue; 10 Mat srcImage2, srcImage3, dstImage; 11 12 // 【1】读取图像 ( 两幅图片需为同样的类型和尺寸 ) 13 srcImage2 = imread("mogu.jpg"); 14 srcImage3 = imread("rain.jpg"); 15 16 if( !srcImage2.data ) { printf("读取srcImage2错误! "); return false; } 17 if( !srcImage3.data ) { printf("读取srcImage3错误! "); return false; } 18 19 // 【2】进行图像混合加权操作 20 betaValue = ( 1.0 - alphaValue ); 21 addWeighted( srcImage2, alphaValue, srcImage3, betaValue, 0.0, dstImage); 22 23 // 【3】显示原图窗口 24 imshow( "<2>线性混合示例窗口【原图】", srcImage2 ); 25 imshow( "<3>线性混合示例窗口【效果图】", dstImage ); 26 27 return true; 28 29 }
初级图像混合
1 //---------------------------------【头文件、命名空间包含部分】------------------------------- 2 // 描述:包含程序所使用的头文件和命名空间 3 //------------------------------------------------------------------------------------------------ 4 #include <opencv2/core/core.hpp> 5 #include <opencv2/highgui/highgui.hpp> 6 #include <iostream> 7 8 using namespace cv; 9 using namespace std; 10 11 12 //-----------------------------------【全局函数声明部分】-------------------------------------- 13 // 描述:全局函数声明 14 //----------------------------------------------------------------------------------------------- 15 bool ROI_AddImage(); 16 bool LinearBlending(); 17 bool ROI_LinearBlending(); 18 void ShowHelpText(); 19 20 //-----------------------------------【main( )函数】-------------------------------------------- 21 // 描述:控制台应用程序的入口函数,我们的程序从这里开始 22 //----------------------------------------------------------------------------------------------- 23 int main( ) 24 { 25 //system调用dos命令 26 //color 6f是用来设置dos窗口颜色的;1设置窗口背景颜色,f设置窗口字体颜色。 27 system("color 6F"); 28 29 ShowHelpText(); 30 31 if(ROI_AddImage( ) && LinearBlending( ) && ROI_LinearBlending( )) 32 { 33 cout<<endl<<" 运行成功,得出了需要的图像"; 34 } 35 36 waitKey(0);//等待键位操作,按下-> 返回键盘的操作值,否则返回-1 37 return 0; 38 } 39 40 41 //-----------------------------------【ShowHelpText( )函数】---------------------------------- 42 // 描述:输出一些帮助信息 43 //---------------------------------------------------------------------------------------------- 44 void ShowHelpText() 45 { 46 //输出欢迎信息和OpenCV版本 47 printf(" 非常感谢购买《OpenCV3编程入门》一书! "); 48 printf(" 此为本书OpenCV3版的第25个配套示例程序 "); 49 printf(" 当前使用的OpenCV版本为:" CV_VERSION ); 50 printf(" ---------------------------------------------------------------------------- "); 51 } 52 53 54 55 56 //----------------------------------【ROI_AddImage( )函数】---------------------------------- 57 // 函数名:ROI_AddImage() 58 // 描述:利用感兴趣区域ROI实现图像叠加 59 //---------------------------------------------------------------------------------------------- 60 bool ROI_AddImage() 61 { 62 63 // 【1】读入图像 64 Mat srcImage1= imread("dota_pa.jpg"); 65 Mat logoImage= imread("dota_logo.jpg"); 66 if( !srcImage1.data ) { printf("读取srcImage1错误~! "); return false; } 67 if( !logoImage.data ) { printf("读取logoImage错误~! "); return false; } 68 69 // 【2】定义一个Mat类型并给其设定ROI区域 70 Mat imageROI= srcImage1(Rect(200,250,logoImage.cols,logoImage.rows)); 71 72 // 【3】加载掩模(必须是灰度图) 73 Mat mask= imread("dota_logo.jpg",0); 74 75 //【4】将掩膜拷贝到ROI 76 logoImage.copyTo(imageROI,mask); 77 78 // 【5】显示结果 79 namedWindow("<1>利用ROI实现图像叠加示例窗口"); 80 imshow("<1>利用ROI实现图像叠加示例窗口",srcImage1); 81 82 return true; 83 } 84 85 86 //---------------------------------【LinearBlending()函数】------------------------------------- 87 // 函数名:LinearBlending() 88 // 描述:利用cv::addWeighted()函数实现图像线性混合 89 //-------------------------------------------------------------------------------------------- 90 bool LinearBlending() 91 { 92 //【0】定义一些局部变量 93 double alphaValue = 0.5; 94 double betaValue; 95 Mat srcImage2, srcImage3, dstImage; 96 97 // 【1】读取图像 ( 两幅图片需为同样的类型和尺寸 ) 98 srcImage2 = imread("mogu.jpg"); 99 srcImage3 = imread("rain.jpg"); 100 101 if( !srcImage2.data ) { printf("读取srcImage2错误! "); return false; } 102 if( !srcImage3.data ) { printf("读取srcImage3错误! "); return false; } 103 104 // 【2】进行图像混合加权操作 105 betaValue = ( 1.0 - alphaValue ); 106 addWeighted( srcImage2, alphaValue, srcImage3, betaValue, 0.0, dstImage); 107 108 // 【3】显示原图窗口 109 imshow( "<2>线性混合示例窗口【原图】", srcImage2 ); 110 imshow( "<3>线性混合示例窗口【效果图】", dstImage ); 111 112 return true; 113 114 } 115 116 //---------------------------------【ROI_LinearBlending()】------------------------------------- 117 // 函数名:ROI_LinearBlending() 118 // 描述:线性混合实现函数,指定区域线性图像混合.利用cv::addWeighted()函数结合定义 119 // 感兴趣区域ROI,实现自定义区域的线性混合 120 //-------------------------------------------------------------------------------------------- 121 bool ROI_LinearBlending() 122 { 123 124 //【1】读取图像 125 Mat srcImage4= imread("dota_pa.jpg",1); 126 Mat logoImage= imread("dota_logo.jpg"); 127 128 if( !srcImage4.data ) { printf("读取srcImage4错误~! "); return false; } 129 if( !logoImage.data ) { printf("读取logoImage错误~! "); return false; } 130 131 //【2】定义一个Mat类型并给其设定ROI区域 132 Mat imageROI; 133 //方法一 134 imageROI= srcImage4(Rect(200,250,logoImage.cols,logoImage.rows)); 135 //方法二 136 //imageROI= srcImage4(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols)); 137 138 //【3】将logo加到原图上 139 addWeighted(imageROI,0.5,logoImage,0.3,0.,imageROI); 140 141 //【4】显示结果 142 imshow("<4>区域线性图像混合示例窗口",srcImage4); 143 144 return true; 145 }
分离颜色通道、多通道图像混合
通道分离:split函数
split函数用于将一个多通道数组分离成几个单通道数组,这里的array用语境翻译为数组或者阵列。
两个原型:
1 void split(const Mat& src, Mat*mvbegin); 2 void split(inputarray m, outputarrayofarrays mv);
(1)src、m是需要进行分离的多通道数组
(2)mv是函数的输出数组或者输出的vector容器
1 Mat imageBlueChannel; 2 Mat imageGreenChannel; 3 Mat imageRedChannel; 4 vector<Mat> channels; 5 6 srcImge4 = imread("dota.jpg"); 7 split(srcImage4,channels);//分离色彩通道 8 9 //把载入的三通道图像转换到三个单通道图像,放到vector<Mat>类型的channel中 10 imageBlueChannel = channels.at(0); 11 imageGreenChannel = channels.at(1); 12 imageRedChannel = channels.at(2);
通道合并:merge函数
两个原型:
1 void merge(const Mat* mv, size_tcount, OutputArray dst); 2 void merge(inputarrayofarrays mv, outputarray dst);
(1)mv,需要被合并的输入矩阵或vector容器的阵列,这个mv参数中所有矩阵必须有一样的尺寸和深度。
(2)count,当mv为空白的C数组时,代表输入矩阵的个数,这个参数显然必须大于1
(3)dst,输出矩阵,和mv[0]有一样的尺寸,并且通道的数量是矩阵阵列中的通道的总数
merge函数将一些数组合并成一个多通道数组。第i个输入数组的元素被视为mv[i]。C一般用一中的Mat::at()函数对某个通道进行存取,channels.at(0)。
Mat::at()方法返回一个引用到指定的数组元素。注意是引用,相当于两者等价。
1 //-----------------------------------【头文件包含部分】--------------------------------------- 2 // 描述:包含程序所依赖的头文件 3 //------------------------------------------------------------------------------------------------ 4 #include <opencv2/core/core.hpp> 5 #include <opencv2/highgui/highgui.hpp> 6 #include <iostream> 7 8 //-----------------------------------【命名空间声明部分】--------------------------------------- 9 // 描述:包含程序所使用的命名空间 10 //------------------------------------------------------------------------------------------------- 11 using namespace cv; 12 using namespace std; 13 14 15 //-----------------------------------【全局函数声明部分】-------------------------------------- 16 // 描述:全局函数声明 17 //----------------------------------------------------------------------------------------------- 18 bool MultiChannelBlending(); 19 void ShowHelpText(); 20 21 22 //-----------------------------------【main( )函数】------------------------------------------ 23 // 描述:控制台应用程序的入口函数,我们的程序从这里开始 24 //----------------------------------------------------------------------------------------------- 25 int main( ) 26 { 27 system("color 9F"); 28 29 ShowHelpText( ); 30 31 if(MultiChannelBlending( )) 32 { 33 cout<<endl<<" 运行成功,得出了需要的图像~! "; 34 } 35 36 waitKey(0); 37 return 0; 38 } 39 40 41 42 //-----------------------------------【ShowHelpText( )函数】---------------------------------- 43 // 描述:输出一些帮助信息 44 //---------------------------------------------------------------------------------------------- 45 void ShowHelpText() 46 { 47 //输出欢迎信息和OpenCV版本 48 printf(" 非常感谢购买《OpenCV3编程入门》一书! "); 49 printf(" 此为本书OpenCV3版的第26个配套示例程序 "); 50 printf(" 当前使用的OpenCV版本为:" CV_VERSION ); 51 printf(" ---------------------------------------------------------------------------- "); 52 } 53 54 55 56 57 58 59 //-----------------------------【MultiChannelBlending( )函数】-------------------------------- 60 // 描述:多通道混合的实现函数 61 //----------------------------------------------------------------------------------------------- 62 bool MultiChannelBlending() 63 { 64 //【0】定义相关变量 65 Mat srcImage; 66 Mat logoImage; 67 vector<Mat> channels; 68 Mat imageBlueChannel; 69 70 //=================【蓝色通道部分】================= 71 // 描述:多通道混合-蓝色分量部分 72 //============================================ 73 74 // 【1】读入图片 75 logoImage= imread("dota_logo.jpg",0);//灰度图是因为,我们是对一个通道进行线性加权的,所以也要将logoimage变成一个通道 76 srcImage= imread("dota_jugg.jpg"); 77 78 if( !logoImage.data ) { printf("Oh,no,读取logoImage错误~! "); return false; } 79 if( !srcImage.data ) { printf("Oh,no,读取srcImage错误~! "); return false; } 80 81 //【2】把一个3通道图像转换成3个单通道图像 82 split(srcImage,channels);//分离色彩通道 83 84 //【3】将原图的蓝色通道引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变 85 imageBlueChannel= channels.at(0); 86 //【4】将原图的蓝色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageBlueChannel中 87 addWeighted( 88 imageBlueChannel(Rect(500,250,logoImage.cols,logoImage.rows)),//需要加权的第一个数组 89 1.0,//第一个数组的权重 90 logoImage,//第二个数组,和第一个数组有相同的尺寸和通道数 91 0.5//第二个数组的权重 92 0,//gamma,加到权重总和上的标量值 93 imageBlueChannel(Rect(500,250,logoImage.cols,logoImage.rows)) 94 ); 95 96 //【5】将三个单通道重新合并成一个三通道 97 merge(channels,srcImage); 98 99 //【6】显示效果图 100 namedWindow(" <1>游戏原画+logo蓝色通道"); 101 imshow(" <1>游戏原画+logo蓝色通道",srcImage); 102 103 104 //=================【绿色通道部分】================= 105 // 描述:多通道混合-绿色分量部分 106 //============================================ 107 108 //【0】定义相关变量 109 Mat imageGreenChannel; 110 111 //【1】重新读入图片 112 logoImage= imread("dota_logo.jpg",0); 113 srcImage= imread("dota_jugg.jpg"); 114 115 if( !logoImage.data ) { printf("读取logoImage错误~! "); return false; } 116 if( !srcImage.data ) { printf("读取srcImage错误~! "); return false; } 117 118 //【2】将一个三通道图像转换成三个单通道图像 119 split(srcImage,channels);//分离色彩通道 120 121 //【3】将原图的绿色通道的引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变 122 imageGreenChannel= channels.at(1); 123 //【4】将原图的绿色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageGreenChannel中 124 addWeighted(imageGreenChannel(Rect(500,250,logoImage.cols,logoImage.rows)),1.0, 125 logoImage,0.5,0.,imageGreenChannel(Rect(500,250,logoImage.cols,logoImage.rows))); 126 127 //【5】将三个独立的单通道重新合并成一个三通道 128 merge(channels,srcImage); 129 130 //【6】显示效果图 131 namedWindow("<2>游戏原画+logo绿色通道"); 132 imshow("<2>游戏原画+logo绿色通道",srcImage); 133 134 135 136 //=================【红色通道部分】================= 137 // 描述:多通道混合-红色分量部分 138 //============================================ 139 140 //【0】定义相关变量 141 Mat imageRedChannel; 142 143 //【1】重新读入图片 144 logoImage= imread("dota_logo.jpg",0); 145 srcImage= imread("dota_jugg.jpg"); 146 147 if( !logoImage.data ) { printf("Oh,no,读取logoImage错误~! "); return false; } 148 if( !srcImage.data ) { printf("Oh,no,读取srcImage错误~! "); return false; } 149 150 //【2】将一个三通道图像转换成三个单通道图像 151 split(srcImage,channels);//分离色彩通道 152 153 //【3】将原图的红色通道引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变 154 imageRedChannel= channels.at(2); 155 //【4】将原图的红色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageRedChannel中 156 addWeighted(imageRedChannel(Rect(500,250,logoImage.cols,logoImage.rows)),1.0, 157 logoImage,0.5,0.,imageRedChannel(Rect(500,250,logoImage.cols,logoImage.rows))); 158 159 //【5】将三个独立的单通道重新合并成一个三通道 160 merge(channels,srcImage); 161 162 //【6】显示效果图 163 namedWindow("<3>游戏原画+logo红色通道 "); 164 imshow("<3>游戏原画+logo红色通道 ",srcImage); 165 166 return true; 167 }
图像对比度、亮度调整
一般图像处理算子都是一个函数,接受一个或多个输入图像,并产生输出图像,下面是算子的一般形式:
图像对比度和亮度的操作,属于比较简单的一种--点操作。点操作的特点:仅仅根据输入像素值,来计算相应的输出像素值。这列算子包括亮度(brightness)、对比度(constrast)、颜色校正(colorcorrection)、变换(transformations)。
两种最常用的点操作(点算子)是乘上一个常数(对比度的调节)再加上一个常数(对应亮度的调节),公式如下:
(1)f(x)表示源图像像素;
(2)g(x)表示输出图像像素
(3)a>0,增益,控制对比度
(4)b,偏置,控制亮度
进一步可以改写成:
访问图片中的像素
1 //三个for循环执行运算: new_image(i,j) = a*image(i,j) + b 2 for(int y = 0; y < image.rows; y++) 3 { 4 for(int x = 0; x < image.cols; x++) 5 { 6 for(int c = 0; c < 3,; c++) 7 { 8 new_image.at<vector>(y,x)[c] = saturate_cast<uchar>( 9 (g_nContrastValue*0.01) * (image.at<vector>(y,x)[c]) + g_nBrightValue 10 ) 11 } 12 } 13 }
访问图像的每一个像素,使用的语法为:image.at<vector>(y,x)[c]
因为计算结果可能会超出像素取值范围(溢出),还可能是非整数,所以用saturate_cast对结果进行转换,确保是有效值。saturate_cast模板函数,用于溢出保护,大致原理如下:
1 if(data<0) 2 data = 0; 3 else if(data > 255) 4 data=255;
示例程序:图像对比度、亮度调节
1 //--------------------------------------【程序说明】------------------------------------------- 2 // 程序说明:《OpenCV3编程入门》OpenCV3版书本配套示例程序27 3 // 程序描述:图像对比度、亮度值调整 4 // 开发测试所用IDE版本:Visual Studio 2010 5 // 开发测试所用OpenCV版本: 3.0 beta 6 // 2014年11月 Created by @浅墨_毛星云 7 // 2014年12月 Revised by @浅墨_毛星云 8 //------------------------------------------------------------------------------------------------ 9 10 11 12 //-----------------------------------【头文件包含部分】--------------------------------------- 13 // 描述:包含程序所依赖的头文件 14 //---------------------------------------------------------------------------------------------- 15 #include <opencv2/core/core.hpp> 16 #include <opencv2/highgui/highgui.hpp> 17 #include "opencv2/imgproc/imgproc.hpp" 18 #include <iostream> 19 20 //-----------------------------------【命名空间声明部分】--------------------------------------- 21 // 描述:包含程序所使用的命名空间 22 //----------------------------------------------------------------------------------------------- 23 using namespace std; 24 using namespace cv; 25 26 27 //-----------------------------------【全局函数声明部分】-------------------------------------- 28 // 描述:全局函数声明 29 //----------------------------------------------------------------------------------------------- 30 static void ContrastAndBright(int, void *); 31 void ShowHelpText(); 32 33 //-----------------------------------【全局变量声明部分】-------------------------------------- 34 // 描述:全局变量声明 35 //----------------------------------------------------------------------------------------------- 36 int g_nContrastValue; //对比度值 37 int g_nBrightValue; //亮度值 38 Mat g_srcImage,g_dstImage; 39 //-----------------------------------【main( )函数】-------------------------------------------- 40 // 描述:控制台应用程序的入口函数,我们的程序从这里开始 41 //----------------------------------------------------------------------------------------------- 42 int main( ) 43 { 44 //改变控制台前景色和背景色 45 system("color 2F"); 46 47 ShowHelpText(); 48 // 读入用户提供的图像 49 g_srcImage = imread( "1.jpg"); 50 if( !g_srcImage.data ) { printf("读取g_srcImage图片错误~! "); return false; } 51 //创建和输入图像一样size和type的全零矩阵 52 g_dstImage = Mat::zeros( g_srcImage.size(), g_srcImage.type() ); 53 54 //设定对比度和亮度的初值 55 g_nContrastValue=80; 56 g_nBrightValue=80; 57 58 //创建窗口 59 namedWindow("【效果图窗口】", 1); 60 61 //创建轨迹条 62 createTrackbar("对比度:",//轨迹条名字 63 "【效果图窗口】",//窗口名字 64 &g_nContrastValue, //对比度当前值 65 300,//对比度最大值 66 ContrastAndBright//回调函数 67 ); 68 createTrackbar("亮 度:", "【效果图窗口】",&g_nBrightValue, 200,ContrastAndBright ); 69 70 //调用回调函数,viod* 默认用0 71 ContrastAndBright(g_nContrastValue,0);//为什么是0??? 72 ContrastAndBright(g_nBrightValue,0); 73 74 //输出一些帮助信息 75 cout<<endl<<" 运行成功,请调整滚动条观察图像效果 " 76 <<" 按下“q”键时,程序退出 "; 77 78 //按下“q”键时,程序退出 79 while(char(waitKey(1)) != 'q') {} 80 return 0; 81 } 82 83 84 85 86 //-----------------------------------【ShowHelpText( )函数】---------------------------------- 87 // 描述:输出一些帮助信息 88 //---------------------------------------------------------------------------------------------- 89 void ShowHelpText() 90 { 91 //输出欢迎信息和OpenCV版本 92 printf(" 非常感谢购买《OpenCV3编程入门》一书! "); 93 printf(" 此为本书OpenCV3版的第27个配套示例程序 "); 94 printf(" 当前使用的OpenCV版本为:" CV_VERSION ); 95 printf(" ---------------------------------------------------------------------------- "); 96 } 97 98 99 //-----------------------------【ContrastAndBright( )函数】------------------------------------ 100 // 描述:改变图像对比度和亮度值的回调函数 101 //----------------------------------------------------------------------------------------------- 102 static void ContrastAndBright(int, void *) 103 { 104 105 // 创建窗口 106 namedWindow("【原始图窗口】", 1); 107 108 // 三个for循环,执行运算 g_dstImage(i,j) = a*g_srcImage(i,j) + b 109 for( int y = 0; y < g_srcImage.rows; y++ ) 110 { 111 for( int x = 0; x < g_srcImage.cols; x++ ) 112 { 113 for( int c = 0; c < 3; c++ ) 114 { 115 g_dstImage.at<Vec3b>(y,x)[c] = saturate_cast<uchar>( (g_nContrastValue*0.01)*( g_srcImage.at<Vec3b>(y,x)[c] ) + g_nBrightValue ); 116 } 117 } 118 } 119 120 // 显示图像 121 imshow("【原始图窗口】", g_srcImage); 122 imshow("【效果图窗口】", g_dstImage); 123 }
离散傅里叶变换DFT
指傅里叶变换在时域和频域上都呈现离散的形式,将时域信号的采样变换为在离散时间傅里叶变换(DTFT)频域的采样。
对有限长的离散信号做DFT,也应当对其经过周期延拓成周期信号再进行变换。
实际应用中,通常采用快速傅里叶变换来高效计算DFT。
原理
对一张图像使用傅里叶变换就是将它分解成正弦和余弦的和的形式。傅里叶变换的作用实际上就是函数分解的工具。
二维图像傅里叶变换公式:
转换之后的频域值是复数,因此结果使用实数图像+虚数图像,或者幅度图像+相位图像。
实际图像处理过程中,仅仅使用幅度图像,幅度图像包含了原图像几乎所有我们需要的几何信息。如果想通过修改幅度图像或者相位图像来间接修改原空间图像,需要使用逆傅里叶变换得到修改后的空间图像。
频域里面,对于一幅图像,高频部分代表了图像的细节、纹理信息;低频部分代表了图像的轮廓信息。
如果图像受到的噪声位于某个特定的频率范围之内,则可以通过滤波器来恢复原来的图像。
傅里叶变换在图像处理中可以做到图像增强和图像去燥,图像分割之边缘检测、图像特征提取,图像压缩等。
dft函数
dft函数是对一维或二维浮点数数组进行正向或反向离散傅里叶变换。
1 void dft(inputarray src, outputarray dst, int flags=0, int nonzeroRows=0);
(1)src,输入矩阵,实数或虚数
(2)dst,运算结果,尺寸和类型取决于标识符,也就是flags
(3)flags,转换标识符,默认0
(4)nonzeroRows,默认值0。