测试代码:opencvsamplescpp utorial_codecorehow_to_scan_images
测试函数耗时
cv::getTickCount() the number of clock cycle
cv::getTickFrequency() the number of cycles per seconds
double t = (double)getTickCount(); // 做点什么 ... t = ((double)getTickCount() - t)/getTickFrequency(); cout << "Times passed in seconds: " << t << endl;
访问像素点
以颜色缩减为例,可通过查找表替换颜色从而缩减存储,查找表构建方法:
uchar table[256]; for (int i = 0; i < 256; ++i) table[i] = divideWith* (i/divideWith);
有以下几种访问像素方法:
1. []运算符 加 查找表
int nRows = I.rows * I.channels(); int nCols = I.cols; if (I.isContinuous()) { nCols *= nRows; nRows = 1; } int i,j; uchar* p; for( i = 0; i < nRows; ++i) { p = I.ptr<uchar>(i); for ( j = 0; j < nCols; ++j) { p[j] = table[p[j]]; } }
我们获取了每一行开始处的指针,然后遍历至该行末尾。如果矩阵是以连续方式存储的,我们只需请求一次指针、然后一路遍历下去就行。彩色图像的情况有必要加以注意:因为三个通道的原因,我们需要遍历的元素数目也是3倍。
2. 迭代器
const int channels = I.channels(); switch(channels) { case 1: { MatIterator_<uchar> it, end; for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it) *it = table[*it]; break; } case 3: { MatIterator_<Vec3b> it, end; for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it) { (*it)[0] = table[(*it)[0]]; (*it)[1] = table[(*it)[1]]; (*it)[2] = table[(*it)[2]]; } } }
在cv::Mat_的子类中,下划线表示这是一个模板类。OpenCV将Mat设计为一个容器,可以这样声明迭代器:
cv::MatIterator_<cv::Vec3b> it; cv::Mat_<cv::Vec3b>::iterator it; // 之后就可以使用begin()和end()等方法了 cv::Mat_<cv::Vec3b>::iterator it= image.begin<cv::Vec3b>();
Mat_类型在使用begin和end方法时可以不加类型:
cv::Mat_<cv::Vec3b> cimage= image; cv::Mat_<cv::Vec3b>::iterator it= cimage.begin(); cv::Mat_<cv::Vec3b>::iterator itend= cimage.end();
3. On-the-fly 动态计算地址:at()函数
const int channels = I.channels(); switch(channels) { case 1: { for( int i = 0; i < I.rows; ++i) for( int j = 0; j < I.cols; ++j ) I.at<uchar>(i,j) = table[I.at<uchar>(i,j)]; break; } case 3: { Mat_<Vec3b> _I = I; for( int i = 0; i < I.rows; ++i) for( int j = 0; j < I.cols; ++j ) { _I(i,j)[0] = table[_I(i,j)[0]]; _I(i,j)[1] = table[_I(i,j)[1]]; _I(i,j)[2] = table[_I(i,j)[2]]; } I = _I; break; } }
注意:当且仅当在 debug 模式下 它会检查你的输入坐标是否有效或者超出范围。
cv::Mat_类型可以直接用operator()访问像素,省略.at(),少写两个字。
cv::Mat_<uchar> im2= image; // im2 refers to image im2(50,100)= 0; // access to row 50 and column 100
4. 核心函数LUT(The core function)
Mat lookUpTable(1, 256, CV_8U); uchar* p = lookUpTable.data; for( int i = 0; i < 256; ++i) p[i] = table[i]; LUT(I, lookUpTable, J);
对于一个给定的值,将其替换成其他的值是一个很常见的操作,OpenCV 提供里一个函数直接实现该操作,并不需要你自己扫描图像,就是:operationsOnArrays:LUT() <lut> ,一个包含于core module的函数。
性能表现
4. LUT > 1. Efficient way > 2. Iterator > 3. On-The-Fly random access
输入:600*450
输出:
Time of reducing with the C operator [] (averaged for 100 runs): 4.05264 milliseconds. Time of reducing with the iterator (averaged for 100 runs): 137.583 milliseconds. Time of reducing with the on-the-fly address generation - at function (averaged for 100 runs): 255.371 milliseconds. Time of reducing with the LUT function (averaged for 100 runs): 3.51129 milliseconds.
结论: 尽量使用 OpenCV 内置函数. 调用LUT 函数可以获得最快的速度. 这是因为OpenCV库可以通过英特尔线程架构启用多线程.