(一)我们将探索以下问题的答案: 1--如何遍历图像中的每一个像素点? 2--OpenCv的矩阵值是如何存储的? 3--如何测试我们所实现算法的性能? 4--查找表是什么?为什么要用它? (二)图像矩阵是如何存储在内存之中的? 1--图像矩阵的大小取决于我们使用的颜色模型,确切的说,取决于所用通道数。如果是灰度图像,矩阵就会像这样:
2--而对于多通道图像来说,矩阵中的列会包含多个子列,其子列个数与通道数相等,例如,RGB颜色模型的矩阵:
3--在这里,我们需要注意的是:子列的通道顺序是反过来的,是BGR,而不是我们数字图像书中常 说的RGB.很多情况下,因为内存够大,可以实现连续存储。连续存储可以提高图像的扫描速度。 (三)颜色空间的缩减 如果矩阵元素存储的是单通道像素,使用C或C++的无符号字符类型,那么像素可能有256个不同的值. 但若是三通道图像,这种存储格式的颜色数就太多了(确切的说,有一千六百多万种).用如此之多的颜色 可能会对我们算法的性能造成严重的影响。其实,有时候,我们仅适用这些颜色的一小部分,就足以达到 同样的效果。 这种情况下,常用的一种方法就是【颜色空间缩减】.其做法是:将现有颜色值除以某个输入值,以获 得较少的颜色数。例如:0到9可取新值0;10到19取值1,以此类推。 uchar(无符号字符,即0到255之间取值的数)类型除以int值,结果仍为char。因为结果是char类型的, 所以,求出来小数也要向下取整。利用这一点,刚才提到在uchar定义域中进行颜色缩减运算就可以表达 为下列形式: Inew=Iold/10*10; 这样的话,简单的颜色缩减算法就可以由以下两步组成: 1--遍历图像矩阵中的每一个像素 2--对像素应用上述公式 值得注意的是,我们这里用到了除法和乘法运算,而这种运算又特别费时,所以,我们应尽可能用代 价较低的加减,赋值等运算替换他们。 (四)计时函数 1--OpenCv为我们提供了两个简单的计时函数---getTickCount()和getTickFrequency() 2--getTickCount()函数---返回CPU自某个事件以来走过的----时钟周期数 3--getTickFrequency()函数返回CUP一秒钟所走的时钟周期数。这样,我们就可以轻松的以秒为单位 对某运算计时 这两个函数组合起来的使用如下所示: //【5】记录其实时间 double timeStartPtr=static_cast<double>(getTickCount()); //【6】调用颜色空间缩减函数 colorReducePtr(srcImg,dstImgPtr,32); //【7】计算运行时间,并输出 double timeExpensePtr=((double)getTickCount()-timeStartPtr)/getTickFrequency(); cout<<"【1】ptr指针操作像素,此方法运行时间为---------->"<<timeExpensePtr<<endl; (五)访问图像中像素的三类方法 任何图像处理算法。都是从操作每个像素开始的。即使我们不会使用OpenCv提供的各种图像 处理函数,只要我们了解图像处理算法的基本原理,也可以写出具有相同功能的程序.在OpenCv中, 提供了三种访问每个像素的方法: 1--指针访问----------ptr<>()函数 2--动态地址计算------at<>()函数 3--迭代器iterator 这三种方法,在访问速度上略有差异,指针比较快。
/********************************************************************************************* 程序功能: 图像像素点操作的三种常用方法的介绍: 1--ptr<>()------Mat类中的模板函数--可以得到图像中任意一行的--------首地址 2--at<>()-------Mat类中的模板函数--可以得到图像中任意指定元素的----地址 3--Iterator-----迭代器操作像素 编写环境: OpenCv2.4.8+VS2010 地点时间: 陕西师范大学 2016.4.25 作者信息: 九月 **********************************************************************************************/ /********************************【头文件.命名空间包含部分】**********************************/ #include<opencv2/core/core.hpp> #include<highgui/highgui.hpp> #include<iostream> using namespace std; using namespace cv; /***************************************【全局函数声明部分】**********************************/ void colorReducePtr(Mat& inputImg,Mat& outputImg,int div); void colorReduceAt(Mat& inputImg,Mat& outputImg,int div); void colorReduceIterator(Mat& inputImg,Mat& outputImg,int div); void matHelp(Mat& inputImg); /********************************************【main函数】*************************************/ int main(int argc,char** argv) { //【1】创建Mat类对象的信息头 Mat srcImg; //【2】创建矩阵体,载入图片并显示 srcImg=imread("D:\\scenery.png",CV_LOAD_IMAGE_COLOR); imshow("原始图像",srcImg); //【3】显示图像的基本信息 matHelp(srcImg); //【4】按照原图的参数规格,来创建效果图 Mat dstImgPtr; dstImgPtr.create(srcImg.rows,srcImg.cols,srcImg.type()); Mat dstImgAt; dstImgAt.create(srcImg.rows,srcImg.cols,srcImg.type()); Mat dstImgIterator; dstImgIterator.create(srcImg.rows,srcImg.cols,srcImg.type()); //【5】记录其实时间 double timeStartPtr=static_cast<double>(getTickCount()); //【6】调用颜色空间缩减函数 colorReducePtr(srcImg,dstImgPtr,32); //【7】计算运行时间,并输出 double timeExpensePtr=((double)getTickCount()-timeStartPtr)/getTickFrequency(); cout<<"【1】ptr指针操作像素,此方法运行时间为---------->"<<timeExpensePtr<<endl; double timeStartAt=static_cast<double>(getTickCount()); colorReduceAt(srcImg,dstImgAt,32); double timeExpenseAt=((double)getTickCount()-timeStartAt)/getTickFrequency(); cout<<"【2】at()动态地址计算,此方法运行时间为--------->"<<timeExpenseAt<<endl; double timeStartIterator=static_cast<double>(getTickCount()); colorReduceIterator(srcImg,dstImgIterator,32); double timeExpenseIterator=((double)getTickCount()-timeStartIterator)/getTickFrequency(); cout<<"【3】Iterator迭代器操作像素,此方法运行时间为--->"<<timeExpenseIterator<<endl; //【8】显示效果图 imshow("效果图",dstImgIterator); waitKey(0); } /*************************************************************************************************** 函数功能: 利用Mat类中的成员函数和数据成员提供一些编程的参考信息 函数参数: Mat& inputImg---输入图像的引用类型 函数返回值: 无 ***************************************************************************************************/ void matHelp(Mat& inputImg) { //【1】图像的行数 int nRows=inputImg.rows; //【2】图像的列数 int nCols=inputImg.cols; //【3】图像的高度 int nHeight=inputImg.size().height; //【4】图像的高度 int nWidth =inputImg.size().width; //【5】图像的通道个数,彩色图像3通道,灰度图像单通道 int nChannels=inputImg.channels(); //【6】图像中的一个像素点位置所含的字节数 int nElemByte=inputImg.elemSize(); //【7】图像中的一行,所含有的字节数 int nColByte=inputImg.step; //【8】图像中像素的总个数 int elemTotal=inputImg.total(); //【9】返回图像的列数*行数---返回图像的尺寸大小 cout<<"【1】图像的行数-------------------------------------------------->"<<nRows<<endl; cout<<"【2】图像的列数-------------------------------------------------->"<<nCols<<endl; cout<<"【3】图像的高度-------------------------------------------------->"<<nHeight<<endl; cout<<"【4】图像的高度-------------------------------------------------->"<<nWidth<<endl; cout<<"【5】图像的通道个数,彩色图像3通道,灰度图像单通道----------------->"<<nChannels<<endl; cout<<"【6】图像中的一个像素点位置所含的字节数-------------------------->"<<nElemByte<<endl; cout<<"【7】图像中的一行,所含有的字节数--------------------------------->"<<nColByte<<endl; cout<<"【8】图像中像素的总个数------------------------------------------>"<<elemTotal<<endl; cout<<"【9】返回图像的列数*行数---返回图像的尺寸大小-------------------->"<<inputImg.size()<<endl; cout<<"【10】此外,Mat类中还有比如:at<>(),ptr<>(),data等重要的成员"<<endl; } /*************************************************************************************************** 函数功能: 1--缩减颜色空间 2--显示使用【指针】访问像素 3--显示Mat类的模板函数ptr<>()---ptr函数可以得到图像任意一行的---首地址 函数参数: Mat& inputImg-----输入图像 Mat& outputImg----输出图像 int div----------颜色空间缩减倍数 函数返回值: 无 ***************************************************************************************************/ void colorReducePtr(Mat& inputImg,Mat& outputImg,int div) { outputImg=inputImg.clone(); //复制图像的信息头和矩阵体到临时变量----->深复制 int nRows=outputImg.rows; //行数 int nCols=outputImg.cols*outputImg.channels(); //列数*通过数=每一行的元素个数 //双循环,遍历所有的像素值 for(int i=0;i<nRows;i++) { uchar* data=outputImg.ptr<uchar>(i); //获取第i行的首地址 for(int j=0;j<nCols;j++) { /***************【开始处理每个像素】****************/ data[j]=data[j]/div*div+div/2; }//for j }//for i } /*************************************************************************************************** 函数功能: 1--缩减颜色空间 2--显示使用【指针】访问像素 3--显示Mat类的模板函数at<>()---at函数可以返回指定数组元素的地址 函数参数: Mat& inputImg-----输入图像 Mat& outputImg----输出图像 int div----------颜色空间缩减倍数 函数返回值: 无 ***************************************************************************************************/ void colorReduceAt(Mat& inputImg,Mat& outputImg,int div) { outputImg=inputImg.clone(); //复制图像的信息头和矩阵体到临时变量----->深复制 int nRows=outputImg.rows; //行数 int nCols=outputImg.cols; //列数 //双循环,遍历所有的像素值 for(int i=0;i<nRows;i++) { for(int j=0;j<nCols;j++) { /***************【开始处理每个像素】****************/ outputImg.at<Vec3b>(i,j)[0]=outputImg.at<Vec3b>(i,j)[0]/div*div+div/2;//蓝色通道 outputImg.at<Vec3b>(i,j)[1]=outputImg.at<Vec3b>(i,j)[1]/div*div+div/2;//绿色通道 outputImg.at<Vec3b>(i,j)[2]=outputImg.at<Vec3b>(i,j)[2]/div*div+div/2;//红色通道 }//for j }//for i } /*************************************************************************************************** 函数功能: 1--缩减颜色空间 2--显示使用【指针】访问像素 3--用迭代器操作像素 函数参数: Mat& inputImg-----输入图像 Mat& outputImg----输出图像 int div----------颜色空间缩减倍数 函数返回值: 无 ***************************************************************************************************/ void colorReduceIterator(Mat& inputImg,Mat& outputImg,int div) { outputImg=inputImg.clone(); //复制图像的信息头和矩阵到outputImg这个临时变量中 //获取迭代器 Mat_<Vec3b>::iterator itBegin=outputImg.begin<Vec3b>(); //初始位置 Mat_<Vec3b>::iterator inEnd =outputImg.end<Vec3b>(); //终止位置 //存取彩色图像像素 for(;itBegin!=inEnd;++itBegin) { (*itBegin)[0]=(*itBegin)[0]/div*div+div/2; (*itBegin)[1]=(*itBegin)[1]/div*div+div/2; (*itBegin)[2]=(*itBegin)[2]/div*div+div/2; } }