图像操作最基础的还是对像素获取及变换,也就相当于对矩阵中的元素的操作,使用不同的方法所用的时间也相差甚大。以下有几种方法:
1、指针操作
(1)双重循环,遍历图像所有的像素值
- <span style="font-size:18px;">for(int i = 0;i < rowNumber;i++) //行循环
- {
- uchar* data = outputImage.ptr<uchar>(i); //<span style="color:#ff0000;">获取第i行的首地址</span>
- for(int j = 0;j < colNumber;j++) //列循环
- {
- // ---------【开始处理每个像素】-------------
- data[j] = data[j]/div*div + div/2;
- // ----------【处理结束】---------------------
- } //行处理结束
- } </span>
(2)更高效的扫描连续图像,把一副M*N(M行,N列)图像看成是1*(M*N)(1行M*N列)。使用方法如下:
- if (img.isContinuous())
- {<pre name="code" class="html">uchar* data = img.data;
- // img.at(i, j)
- data = img.data + i * img.step + j * img.elemSize();</pre><br>
- nc = img.rows*img.cols*img.channels();}
- <pre></pre>
- <pre></pre>
- <pre></pre>
(3)更低级的指针操作就是使用Mat里的data指针,使用方法:
- uchar* data = img.data;
- // img.at(i, j)
- data = img.data + i * img.step + j * img.elemSize();
2.Mat成员函数,存取单个像素值
(1)最通常的方法就是
- //参数准备
- outputImage = inputImage.clone(); //拷贝实参到临时变量
- int rowNumber = outputImage.rows; //行数
- int colNumber = outputImage.cols; //列数
- //存取彩色图像像素
- for(int i = 0;i < rowNumber;i++)
- {
- for(int j = 0;j < colNumber;j++)
- {
- // ------------------------【开始处理每个像素】--------------------
- outputImage.at<Vec3b>(i,j)[0] = outputImage.at<Vec3b>(i,j)[0]/div*div + div/2; //蓝色通道
- outputImage.at<Vec3b>(i,j)[1] = outputImage.at<Vec3b>(i,j)[1]/div*div + div/2; //绿色通道
- outputImage.at<Vec3b>(i,j)[2] = outputImage.at<Vec3b>(i,j)[2]/div*div + div/2; //红是通道
- // -------------------------【处理结束】----------------------------
- } // 行处理结束
- }
(2)也可以考虑使用的Mat_类,使用重载操作符()实现取元素的操作。
- cv::Mat_<uchar> im2= img; // im2 refers to image
- im2(50,100)= 0; // access to row 50 and column 100
3.用迭代器iterator扫描图像
和C++STL里的迭代器类似,Mat的迭代器与之是兼容的。是MatIterator_。声明方法如下:
- cv::MatIterator_<Vec3b> it;
- cv::Mat_<Vec3b>::iterator it;
扫描图像的方法如下:
- //参数准备
- outputImage = inputImage.clone(); //拷贝实参到临时变量
- //获取迭代器
- Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>(); //初始位置的迭代器
- Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>(); //终止位置的迭代器
- //存取彩色图像像素
- for(;it != itend;++it)
- {
- // ------------------------【开始处理每个像素】--------------------
- (*it)[0] = (*it)[0]/div*div + div/2;
- (*it)[1] = (*it)[1]/div*div + div/2;
- (*it)[2] = (*it)[2]/div*div + div/2;
- // ------------------------【处理结束】----------------------------
- }
4.整行整列像素值的赋值
对于整行或者整列的数据,可以考虑这种方式处理
- img.row(i).setTo(Scalar(255));
- img.col(j).setTo(Scalar(255));
【3】记录起始时间
double time0 = static_cast<double>(getTickCount());
//【4】调用颜色空间缩减函数
colorReduce(srcImage,dstImage,1);
//【5】计算运行时间并输出
time0 = ((double
5.效率计算(时间)
使用getTickCount、getTickFrequency函数测试速度。
- //记录起始时间
- double time0 = static_cast<double>(getTickCount());
- //调用函数
- //计算运行时间并输出
- time0 = ((double)getTickCount() - time0)/getTickFrequency();
(1)内存分配是个耗时的工作,优化之;
(2)在循环中重复计算已经得到的值,是个费时的工作,优化之;举例:
- int nc = img.cols * img.channels();
- for (int i=0; i<nc; i++)
- {.......}
- //**************************
- for (int i=0; i<img.cols * img.channels(); i++)
- {......}
(3)使用迭代器也会是速度变慢,但迭代器的使用可以减少程序错误的发生几率,考虑这个因素,可以酌情优化
(4)at操作要比指针的操作慢很多,所以对于不连续数据或者单个点处理,可以考虑at操作,对于连续的大量数据,不要使用它
(5)扫描连续图像的做法可能是把W*H的一副图像看成是一个1*(w*h)的一个一维数组这种办法也可以提高速度。短的循环比长循环更高效,即使他们的操作数是相同的
遍历图像像素的14种方法
- #include <iostream>
- #include <opencv2/core/core.hpp>
- #include <opencv2/highgui/highgui.hpp>
- using namespace cv;
- using namespace std;
- //---------------------------------【宏定义部分】---------------------------------------------
- // 描述:包含程序所使用宏定义
- //-------------------------------------------------------------------------------------------------
- #define NTESTS 14
- #define NITERATIONS 20
- //----------------------------------------- 【方法一】-------------------------------------------
- // 说明:利用.ptr 和 []
- //-------------------------------------------------------------------------------------------------
- void colorReduce0(Mat &image, int div=64) {
- int nl= image.rows; //行数
- int nc= image.cols * image.channels(); //每行元素的总元素数量
- for (int j=0; j<nl; j++)
- {
- uchar* data= image.ptr<uchar>(j);
- for (int i=0; i<nc; i++)
- {
- //-------------开始处理每个像素-------------------
- data[i]= data[i]/div*div + div/2;
- //-------------结束像素处理------------------------
- } //单行处理结束
- }
- }
- //-----------------------------------【方法二】-------------------------------------------------
- // 说明:利用 .ptr 和 * ++
- //-------------------------------------------------------------------------------------------------
- void colorReduce1(Mat &image, int div=64) {
- int nl= image.rows; //行数
- int nc= image.cols * image.channels(); //每行元素的总元素数量
- for (int j=0; j<nl; j++)
- {
- uchar* data= image.ptr<uchar>(j);
- for (int i=0; i<nc; i++)
- {
- //-------------开始处理每个像素-------------------
- *data++= *data/div*div + div/2;
- //-------------结束像素处理------------------------
- } //单行处理结束
- }
- }
- //-----------------------------------------【方法三】-------------------------------------------
- // 说明:利用.ptr 和 * ++ 以及模操作
- //-------------------------------------------------------------------------------------------------
- void colorReduce2(Mat &image, int div=64) {
- int nl= image.rows; //行数
- int nc= image.cols * image.channels(); //每行元素的总元素数量
- for (int j=0; j<nl; j++)
- {
- uchar* data= image.ptr<uchar>(j);
- for (int i=0; i<nc; i++)
- {
- //-------------开始处理每个像素-------------------
- int v= *data;
- *data++= v - v%div + div/2;
- //-------------结束像素处理------------------------
- } //单行处理结束
- }
- }
- //----------------------------------------【方法四】---------------------------------------------
- // 说明:利用.ptr 和 * ++ 以及位操作
- //----------------------------------------------------------------------------------------------------
- void colorReduce3(Mat &image, int div=64) {
- int nl= image.rows; //行数
- int nc= image.cols * image.channels(); //每行元素的总元素数量
- int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
- //掩码值
- uchar mask= 0xFF<<n; // e.g. 对于 div=16, mask= 0xF0
- for (int j=0; j<nl; j++) {
- uchar* data= image.ptr<uchar>(j);
- for (int i=0; i<nc; i++) {
- //------------开始处理每个像素-------------------
- *data++= *data&mask + div/2;
- //-------------结束像素处理------------------------
- } //单行处理结束
- }
- }
- //----------------------------------------【方法五】----------------------------------------------
- // 说明:利用指针算术运算
- //---------------------------------------------------------------------------------------------------
- void colorReduce4(Mat &image, int div=64) {
- int nl= image.rows; //行数
- int nc= image.cols * image.channels(); //每行元素的总元素数量
- int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
- int step= image.step; //有效宽度
- //掩码值
- uchar mask= 0xFF<<n; // e.g. 对于 div=16, mask= 0xF0
- //获取指向图像缓冲区的指针
- uchar *data= image.data;
- for (int j=0; j<nl; j++)
- {
- for (int i=0; i<nc; i++)
- {
- //-------------开始处理每个像素-------------------
- *(data+i)= *data&mask + div/2;
- //-------------结束像素处理------------------------
- } //单行处理结束
- data+= step; // next line
- }
- }
- //---------------------------------------【方法六】----------------------------------------------
- // 说明:利用 .ptr 和 * ++以及位运算、image.cols * image.channels()
- //-------------------------------------------------------------------------------------------------
- void colorReduce5(Mat &image, int div=64) {
- int nl= image.rows; //行数
- int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
- //掩码值
- uchar mask= 0xFF<<n; // e.g. 例如div=16, mask= 0xF0
- for (int j=0; j<nl; j++)
- {
- uchar* data= image.ptr<uchar>(j);
- for (int i=0; i<image.cols * image.channels(); i++)
- {
- //-------------开始处理每个像素-------------------
- *data++= *data&mask + div/2;
- //-------------结束像素处理------------------------
- } //单行处理结束
- }
- }
- // -------------------------------------【方法七】----------------------------------------------
- // 说明:利用.ptr 和 * ++ 以及位运算(continuous)
- //-------------------------------------------------------------------------------------------------
- void colorReduce6(Mat &image, int div=64) {
- int nl= image.rows; //行数
- int nc= image.cols * image.channels(); //每行元素的总元素数量
- if (image.isContinuous())
- {
- //无填充像素
- nc= nc*nl;
- nl= 1; // 为一维数列
- }
- int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
- //掩码值
- uchar mask= 0xFF<<n; // e.g. 比如div=16, mask= 0xF0
- for (int j=0; j<nl; j++) {
- uchar* data= image.ptr<uchar>(j);
- for (int i=0; i<nc; i++) {
- //-------------开始处理每个像素-------------------
- *data++= *data&mask + div/2;
- //-------------结束像素处理------------------------
- } //单行处理结束
- }
- }
- //------------------------------------【方法八】------------------------------------------------
- // 说明:利用 .ptr 和 * ++ 以及位运算 (continuous+channels)
- //-------------------------------------------------------------------------------------------------
- void colorReduce7(Mat &image, int div=64) {
- int nl= image.rows; //行数
- int nc= image.cols ; //列数
- if (image.isContinuous())
- {
- //无填充像素
- nc= nc*nl;
- nl= 1; // 为一维数组
- }
- int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
- //掩码值
- uchar mask= 0xFF<<n; // e.g. 比如div=16, mask= 0xF0
- for (int j=0; j<nl; j++) {
- uchar* data= image.ptr<uchar>(j);
- for (int i=0; i<nc; i++) {
- //-------------开始处理每个像素-------------------
- *data++= *data&mask + div/2;
- *data++= *data&mask + div/2;
- *data++= *data&mask + div/2;
- //-------------结束像素处理------------------------
- } //单行处理结束
- }
- }
- // -----------------------------------【方法九】 ------------------------------------------------
- // 说明:利用Mat_ iterator
- //-------------------------------------------------------------------------------------------------
- void colorReduce8(Mat &image, int div=64) {
- //获取迭代器
- Mat_<Vec3b>::iterator it= image.begin<Vec3b>();
- Mat_<Vec3b>::iterator itend= image.end<Vec3b>();
- for ( ; it!= itend; ++it) {
- //-------------开始处理每个像素-------------------
- (*it)[0]= (*it)[0]/div*div + div/2;
- (*it)[1]= (*it)[1]/div*div + div/2;
- (*it)[2]= (*it)[2]/div*div + div/2;
- //-------------结束像素处理------------------------
- }//单行处理结束
- }
- //-------------------------------------【方法十】-----------------------------------------------
- // 说明:利用Mat_ iterator以及位运算
- //-------------------------------------------------------------------------------------------------
- void colorReduce9(Mat &image, int div=64) {
- // div必须是2的幂
- int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
- //掩码值
- uchar mask= 0xFF<<n; // e.g. 比如 div=16, mask= 0xF0
- // 获取迭代器
- Mat_<Vec3b>::iterator it= image.begin<Vec3b>();
- Mat_<Vec3b>::iterator itend= image.end<Vec3b>();
- //扫描所有元素
- for ( ; it!= itend; ++it)
- {
- //-------------开始处理每个像素-------------------
- (*it)[0]= (*it)[0]&mask + div/2;
- (*it)[1]= (*it)[1]&mask + div/2;
- (*it)[2]= (*it)[2]&mask + div/2;
- //-------------结束像素处理------------------------
- }//单行处理结束
- }
- //------------------------------------【方法十一】---------------------------------------------
- // 说明:利用Mat Iterator_
- //-------------------------------------------------------------------------------------------------
- void colorReduce10(Mat &image, int div=64) {
- //获取迭代器
- Mat_<Vec3b> cimage= image;
- Mat_<Vec3b>::iterator it=cimage.begin();
- Mat_<Vec3b>::iterator itend=cimage.end();
- for ( ; it!= itend; it++) {
- //-------------开始处理每个像素-------------------
- (*it)[0]= (*it)[0]/div*div + div/2;
- (*it)[1]= (*it)[1]/div*div + div/2;
- (*it)[2]= (*it)[2]/div*div + div/2;
- //-------------结束像素处理------------------------
- }
- }
- //--------------------------------------【方法十二】--------------------------------------------
- // 说明:利用动态地址计算配合at
- //-------------------------------------------------------------------------------------------------
- void colorReduce11(Mat &image, int div=64) {
- int nl= image.rows; //行数
- int nc= image.cols; //列数
- for (int j=0; j<nl; j++)
- {
- for (int i=0; i<nc; i++)
- {
- //-------------开始处理每个像素-------------------
- image.at<Vec3b>(j,i)[0]= image.at<Vec3b>(j,i)[0]/div*div + div/2;
- image.at<Vec3b>(j,i)[1]= image.at<Vec3b>(j,i)[1]/div*div + div/2;
- image.at<Vec3b>(j,i)[2]= image.at<Vec3b>(j,i)[2]/div*div + div/2;
- //-------------结束像素处理------------------------
- } //单行处理结束
- }
- }
- //----------------------------------【方法十三】-----------------------------------------------
- // 说明:利用图像的输入与输出
- //-------------------------------------------------------------------------------------------------
- void colorReduce12(const Mat &image, //输入图像
- Mat &result, // 输出图像
- int div=64) {
- int nl= image.rows; //行数
- int nc= image.cols ; //列数
- //准备好初始化后的Mat给输出图像
- result.create(image.rows,image.cols,image.type());
- //创建无像素填充的图像
- nc= nc*nl;
- nl= 1; //单维数组
- int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
- //掩码值
- uchar mask= 0xFF<<n; // e.g.比如div=16, mask= 0xF0
- for (int j=0; j<nl; j++) {
- uchar* data= result.ptr<uchar>(j);
- const uchar* idata= image.ptr<uchar>(j);
- for (int i=0; i<nc; i++) {
- //-------------开始处理每个像素-------------------
- *data++= (*idata++)&mask + div/2;
- *data++= (*idata++)&mask + div/2;
- *data++= (*idata++)&mask + div/2;
- //-------------结束像素处理------------------------
- } //单行处理结束
- }
- }
- //--------------------------------------【方法十四】-------------------------------------------
- // 说明:利用操作符重载
- //-------------------------------------------------------------------------------------------------
- void colorReduce13(Mat &image, int div=64) {
- int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
- //掩码值
- uchar mask= 0xFF<<n; // e.g. 比如div=16, mask= 0xF0
- //进行色彩还原
- image=(image&Scalar(mask,mask,mask))+Scalar(div/2,div/2,div/2);
- }
- //-----------------------------------【ShowHelpText( )函数】-----------------------------
- // 描述:输出一些帮助信息
- //----------------------------------------------------------------------------------------------
- void ShowHelpText()
- {
- //输出欢迎信息和OpenCV版本
- printf("\n\n\t\t\t非常感谢购买《OpenCV3编程入门》一书!\n");
- printf("\n\n\t\t\t此为本书OpenCV3版的第24个配套示例程序\n");
- printf("\n\n\t\t\t 当前使用的OpenCV版本为:" CV_VERSION );
- printf("\n\n ----------------------------------------------------------------------------\n");
- printf("\n\n正在进行存取操作,请稍等……\n\n");
- }
- //-----------------------------------【main( )函数】--------------------------------------------
- // 描述:控制台应用程序的入口函数,我们的程序从这里开始
- //-------------------------------------------------------------------------------------------------
- int main( )
- {
- int64 t[NTESTS],tinit;
- Mat image0;
- Mat image1;
- Mat image2;
- system("color 4F");
- ShowHelpText();
- image0= imread("1.png");
- if (!image0.data)
- return 0;
- //时间值设为0
- for (int i=0; i<NTESTS; i++)
- t[i]= 0;
- // 多次重复测试
- int n=NITERATIONS;
- for (int k=0; k<n; k++)
- {
- cout << k << " of " << n << endl;
- image1= imread("1.png");
- //【方法一】利用.ptr 和 []
- tinit= getTickCount();
- colorReduce0(image1);
- t[0]+= getTickCount()-tinit;
- //【方法二】利用 .ptr 和 * ++
- image1= imread("1.png");
- tinit= getTickCount();
- colorReduce1(image1);
- t[1]+= getTickCount()-tinit;
- //【方法三】利用.ptr 和 * ++ 以及模操作
- image1= imread("1.png");
- tinit= getTickCount();
- colorReduce2(image1);
- t[2]+= getTickCount()-tinit;
- //【方法四】 利用.ptr 和 * ++ 以及位操作
- image1= imread("1.png");
- tinit= getTickCount();
- colorReduce3(image1);
- t[3]+= getTickCount()-tinit;
- //【方法五】 利用指针的算术运算
- image1= imread("1.png");
- tinit= getTickCount();
- colorReduce4(image1);
- t[4]+= getTickCount()-tinit;
- //【方法六】利用 .ptr 和 * ++以及位运算、image.cols * image.channels()
- image1= imread("1.png");
- tinit= getTickCount();
- colorReduce5(image1);
- t[5]+= getTickCount()-tinit;
- //【方法七】利用.ptr 和 * ++ 以及位运算(continuous)
- image1= imread("1.png");
- tinit= getTickCount();
- colorReduce6(image1);
- t[6]+= getTickCount()-tinit;
- //【方法八】利用 .ptr 和 * ++ 以及位运算 (continuous+channels)
- image1= imread("1.png");
- tinit= getTickCount();
- colorReduce7(image1);
- t[7]+= getTickCount()-tinit;
- //【方法九】 利用Mat_ iterator
- image1= imread("1.png");
- tinit= getTickCount();
- colorReduce8(image1);
- t[8]+= getTickCount()-tinit;
- //【方法十】 利用Mat_ iterator以及位运算
- image1= imread("1.png");
- tinit= getTickCount();
- colorReduce9(image1);
- t[9]+= getTickCount()-tinit;
- //【方法十一】利用Mat Iterator_
- image1= imread("1.png");
- tinit= getTickCount();
- colorReduce10(image1);
- t[10]+= getTickCount()-tinit;
- //【方法十二】 利用动态地址计算配合at
- image1= imread("1.png");
- tinit= getTickCount();
- colorReduce11(image1);
- t[11]+= getTickCount()-tinit;
- //【方法十三】 利用图像的输入与输出
- image1= imread("1.png");
- tinit= getTickCount();
- Mat result;
- colorReduce12(image1, result);
- t[12]+= getTickCount()-tinit;
- image2= result;
- //【方法十四】 利用操作符重载
- image1= imread("1.png");
- tinit= getTickCount();
- colorReduce13(image1);
- t[13]+= getTickCount()-tinit;
- //------------------------------
- }
- //输出图像
- imshow("原始图像",image0);
- imshow("结果",image2);
- imshow("图像结果",image1);
- // 输出平均执行时间
- cout << endl << "-------------------------------------------" << endl << endl;
- cout << "\n【方法一】利用.ptr 和 []的方法所用时间为 " << 1000.*t[0]/getTickFrequency()/n << "ms" << endl;
- cout << "\n【方法二】利用 .ptr 和 * ++ 的方法所用时间为" << 1000.*t[1]/getTickFrequency()/n << "ms" << endl;
- cout << "\n【方法三】利用.ptr 和 * ++ 以及模操作的方法所用时间为" << 1000.*t[2]/getTickFrequency()/n << "ms" << endl;
- cout << "\n【方法四】利用.ptr 和 * ++ 以及位操作的方法所用时间为" << 1000.*t[3]/getTickFrequency()/n << "ms" << endl;
- cout << "\n【方法五】利用指针算术运算的方法所用时间为" << 1000.*t[4]/getTickFrequency()/n << "ms" << endl;
- cout << "\n【方法六】利用 .ptr 和 * ++以及位运算、channels()的方法所用时间为" << 1000.*t[5]/getTickFrequency()/n << "ms" << endl;
- cout << "\n【方法七】利用.ptr 和 * ++ 以及位运算(continuous)的方法所用时间为" << 1000.*t[6]/getTickFrequency()/n << "ms" << endl;
- cout << "\n【方法八】利用 .ptr 和 * ++ 以及位运算 (continuous+channels)的方法所用时间为" << 1000.*t[7]/getTickFrequency()/n << "ms" << endl;
- cout << "\n【方法九】利用Mat_ iterator 的方法所用时间为" << 1000.*t[8]/getTickFrequency()/n << "ms" << endl;
- cout << "\n【方法十】利用Mat_ iterator以及位运算的方法所用时间为" << 1000.*t[9]/getTickFrequency()/n << "ms" << endl;
- cout << "\n【方法十一】利用Mat Iterator_的方法所用时间为" << 1000.*t[10]/getTickFrequency()/n << "ms" << endl;
- cout << "\n【方法十二】利用动态地址计算配合at 的方法所用时间为" << 1000.*t[11]/getTickFrequency()/n << "ms" << endl;
- cout << "\n【方法十三】利用图像的输入与输出的方法所用时间为" << 1000.*t[12]/getTickFrequency()/n << "ms" << endl;
- cout << "\n【方法十四】利用操作符重载的方法所用时间为" << 1000.*t[13]/getTickFrequency()/n << "ms" << endl;
- waitKey();
- return 0;
- }
参考:
http://blog.csdn.net/yang_xian521/article/details/7182185-
顶
- 0
-
踩