访问图像中的像素[OpenCV 笔记16]
再更一发好久没更过的OpenCV,不过其实写到这个部分对计算机视觉算法有所了解的应该可以做到用什么查什么了,所以后面可能会更的慢一点吧,既然开了新坑,还是机器学习更有研究价值吧。。。
图像在内存中的存储方式
灰度图像
RGB图像,矩阵的列会包含多个子列
因为内存足够大,可以实现连续存储,因此,图像中的各行就能一行一行地连接起来,形成一个长行。连续存储,有助于提高图像扫面速度,可以使用isContinuous()来判断矩阵是否是连续存储。
颜色空间缩减
对于三通道图像,一个像素对应的颜色有一千六百多万种,用如此多的颜色可能会影响算法性能。颜色空间缩减即用颜色中具有代表性的一部分表示相近颜色,做法是:将现有颜色空间值除以某个输入值,以获得较少的颜色数。对每个像素进行乘除操作也需要浪费一定的时间(加,减,赋值等代价较低),对于较大的图像,可以预先计算所有可能的值,建立 look up table,
int divideWidth = 10; uchar table[256]; for (int i = 0; i < 256; ++i) table[i] = divideWidth * (i/divideWidth);
然后遍历图像矩阵的每个像素,对像素应用公式:
p[j] = table[p[j]];
OpenCV官方提供了函数进行图像元素查找、扫描与操作图像
void cv::LUT ( InputArray src, InputArray lut, OutputArray dst )
进行look-up table转换操作,输出矩阵将被赋值为
dst(I)<-lut(src(I)+d)
其中,当src为CV_8U时,d=0,src为CV_8S时,d=128(用于把look up table输入转换到0~255)
- src:矩阵输入,矩阵元素为8-bit色彩值。
- lut:256个色彩值的look-up table,如果src为多通道函数,lut可为单通道(src每通道的处理方式相同)或与src的通道数相同。
- dst:与输入矩阵大小、通道数相同的输出矩阵src, 类型与lut相同(CV_8U或CV_8S)
用法示例如下,
Mat lookUpTable(1, 256, CV_8U); uchar* p = lookUpTable.data; for(int i = 0; i < 256; ++i) p[i] = table[i]; for(int i=0; i < times; ++i) LUT(Input,lookUpTable, Output);
计时函数
int64 getTickCount()
返回CPU自某个事件(如启动电脑)以来走过的时钟周期
double getTickFrequency()
返回CPU一秒钟所走过的时钟周期数,这样我们就可以以秒为单位对某运算计时
double time0 = stactic_cast<double>(getTickCount()); time0 = ((double) getTickCount() - time0) / getTickFrequency(); cout << "Runing time: " << time0 << "s" << endl;
访问图像中像素的三类方法
用指针访问像素
利用C中的操作符[]或*,这种方法最快,但比较抽象。
用迭代器操作像素
类似于STL中的用法,只需获取图像矩阵的begin和end,然后迭代从begin直至end。比前一种方法安全,不会越界,但速度慢。
动态地址计算
简单明了,直接用at(y,x)找到像素点,但是速度最慢。
上述三种方法的事例程序如下:
#include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> // function void colorReduce(cv::Mat& inputImage, cv::Mat& outputImage, int div); // main int main( int argc, char** argv ) { // Load image cv::Mat srcImage = cv::imread("1.jpg"); imshow("orginal image", srcImage); // alocate space for output image cv::Mat dstImage; dstImage.create(srcImage.rows, srcImage.cols, srcImage.type()); // get the starting time double time0 = static_cast<double>(cv::getTickCount()); // call color space reduction function colorReduce(srcImage, dstImage, 32); // estimate runing time time0 = ((double)cv::getTickCount() - time0)/cv::getTickFrequency(); std::cout << "Running time for this method: " << time0 << "s." << std::endl; imshow("Lab Space", dstImage); cv::waitKey(0); return 0; } #define USESUBS #ifdef USEPOINTER void colorReduce(cv::Mat& inputImage, cv::Mat& outputImage, int div) { // set parameters outputImage = inputImage.clone(); int rowNumber = outputImage.rows; int colNumber = outputImage.cols*outputImage.channels(); for(int i = 0;i < rowNumber;i++) { uchar* data = outputImage.ptr<uchar>(i); // get the address of the ith row for(int j=0; j<colNumber; j++) { data[j] = data[j]/div*div + div/2; } } } #endif #ifdef USEITERATOR void colorReduce(cv::Mat& inputImage, cv::Mat& outputImage, int div) { // set parameters outputImage = inputImage.clone(); // get the iterator cv::Mat_<cv::Vec3b>::iterator it = outputImage.begin<cv::Vec3b>(); cv::Mat_<cv::Vec3b>::iterator itend = outputImage.end<cv::Vec3b>(); // save new pixel colors for(;it != itend; ++it) { (*it)[0] = (*it)[0]/div*div + div/2; // B (*it)[1] = (*it)[1]/div*div + div/2; // G (*it)[2] = (*it)[2]/div*div + div/2; // R } } #endif #ifdef USESUBS void colorReduce(cv::Mat& inputImage, cv::Mat& outputImage, int div) { // set parameters outputImage = inputImage.clone(); int rowNumber = outputImage.rows; int colNumber = outputImage.cols; // save new pixel colors for(int i=0; i<rowNumber; i++) { for(int j=0; j<colNumber; j++) { outputImage.at<cv::Vec3b>(i,j)[0] = outputImage.at<cv::Vec3b>(i,j)[0]/div*div + div/2; // B outputImage.at<cv::Vec3b>(i,j)[1] = outputImage.at<cv::Vec3b>(i,j)[1]/div*div + div/2; // G outputImage.at<cv::Vec3b>(i,j)[2] = outputImage.at<cv::Vec3b>(i,j)[2]/div*div + div/2; // R } } } #endif /* Outputs: USEPOINTER Running time for this method: 0.0162628s. USEITERATOR Running time for this method: 0.0362183s. USESUBS Running time for this method: 0.0518141s. */