首先推荐一本书:《OpenCV 2 Computer Vision Application Programming Cookbook》网上可以下载到这本书的英文版,貌似没有翻译的。这本书的特点是里面的程序不是那种为了演示函数功能而写的面向过程的小程序,而是用面向对象的思路写的大程序,不过他是一步一步教你写出来的,也不要有太大压力。
任何图像处理算法,都是从操作每个像素开始的。即使你不会使用OpenCV提供的各种图像处理函数,只要你了解图像处理算法的基本原理,也可以写出具有相同功能的程序。在OpenCV中,提供了种访问每个像素的方法:使用at方法、使用迭代器、使用指针。
这三种方法在访问速度上略有差异。debug模式下,这种差异非常明显,不过在release模式下,这种差异就不太明显了。我们通过一组程序来说明这几种方法,程序的目的是减少图像中颜色的数量,比如原来的图像是是256中颜色,我希望将它变成64种颜色,那我只需要将原来的颜色除以4(整除)以后再乘以4就可以了。
先看主程序:
- #include <opencv2/core/core.hpp>
- #include <opencv2/highgui/highgui.hpp>
- #include <iostream>
- using namespace std;
- using namespace cv;
- void colorReduce(Mat& inputImage, Mat& outputImage, int div);
- int main()
- {
- Mat image = imread("D:/picture/img.tif");
- imshow("源图像",image);
- Mat result;//用来保存结果
- result.create(image.rows,image.cols,image.type());//它的大小、类型与原图片相匹配
- double duration;
- duration = static_cast<double>(cv::getTickCount());
- colorReduce(image,result,64);
- duration = static_cast<double>(cv::getTickCount())-duration;
- duration /= cv::getTickFrequency(); // the elapsed time in m
- cout<<"time is"<<duration<<endl;
- imshow("显示结果",result);
- waitKey(0);
- }
主程序中调用colorReduce函数来完成减少颜色的工作:
下面是使用at方法的colorReduce函数,这种方法简洁明了,符合大家对像素的直观认识。运行时间为0.334131。
- void colorReduce(Mat& inputImage, Mat& outputImage, int div)
- {
- outputImage = inputImage.clone();
- int rows = outputImage.rows;
- int cols = outputImage.cols;
- for(int i = 0;i < rows;i++)
- {
- for(int j = 0;j < cols;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;
- }
- }
- }
下面是使用迭代器的colorReduce函数,这种方法与STL库的用法类似。运行时间为0.375629
- void colorReduce(Mat& inputImage, Mat& outputImage, int div)
- {
- outputImage = inputImage.clone();
- //模板必须指明数据类型
- Mat_<Vec3b>::iterator it = inputImage.begin<Vec3b>();
- Mat_<Vec3b>::iterator itend = inputImage.end<Vec3b>();
- //也可以通过指明cimage类型的方法不写begin和end的类型
- Mat_<Vec3b> cimage= outputImage;
- Mat_<Vec3b>::iterator itout = cimage.begin();
- Mat_<Vec3b>::iterator itoutend = cimage.end();
- for(;it != itend;it++,itout++)
- {
- (*itout)[0] = (*it)[0]/div*div + div/2;
- (*itout)[1] = (*it)[1]/div*div + div/2;
- (*itout)[2] = (*it)[2]/div*div + div/2;
- }
- }
最后是使用指针的方法,这种方法最快,但是略有点抽象。运行时间为0.00705378!
- void colorReduce(Mat& inputImage, Mat& outputImage, int div)
- {
- outputImage = inputImage.clone();
- int rows = outputImage.rows;
- int cols = outputImage.cols*outputImage.channels();
- for(int i = 0;i < rows;i++)
- {
- uchar* data = inputImage.ptr<uchar>(i);
- uchar* dataout = outputImage.ptr<uchar>(i);
- for(int j = 0;j < cols;j++)
- {
- dataout[j] = dataout[j]/div*div + div/2;
- }
- }
- }
顺便说一句,OpenCV中的彩色图像不是以RGB的顺序存放的,而是BGR,所以程序中的outputImage.at<Vec3b>(i,j)[0]代表的是该点的B分量。同理还有(*it)[0]。