opencv4 学习 03 像素的遍历方法
#include "pch.h" #include <opencv2/core.hpp> #include <opencv2/imgcodecs.hpp> #include <opencv2/highgui.hpp> #include <iostream> using namespace cv; using namespace std; // 效率高,使用行指针的遍历方式 Mat& scanImageAndReduce(Mat& I, const uchar* const table) { CV_Assert(I.depth() == CV_8U); int channels = I.channels(); int nRows = I.rows; int nCols = I.cols * channels; 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]]; } } return I; } // 使用 iterator,更稳定 Mat& scanImageAndReduceIterator(Mat& I, const uchar* const table) { CV_Assert(I.depth() == CV_8U); 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]]; } } } return I; } // 使用 cv::Mat::at() 函数, 耗时,效率低 Mat& scanImageAndReduceRandomAccess(Mat& I, const uchar* const table) { CV_Assert(I.depth() == CV_8U); 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.at<Vec3b>(i, j)[0] = table[I.at<Vec3b>(i, j)[0]]; I.at<Vec3b>(i, j)[1] = table[I.at<Vec3b>(i, j)[1]]; I.at<Vec3b>(i, j)[2] = table[I.at<Vec3b>(i, j)[2]]; } I = _I; break; } } return I; } // cv::LUT() 函数的使用 Mat coreFunction(Mat& I, const uchar* const table) { Mat output; Mat lookUpTable(1, 256, CV_8U); uchar* p = lookUpTable.ptr(); for (int i = 0; i < 256; ++i) p[i] = table[i]; LUT(I, lookUpTable, output); return output; } int main(int argc, char** argv) { Mat image = imread("G:\\tools\\opencv4\\workspace\\opencv01\\001.jpg"); Mat I = image.clone(); // 对图像的像素范围做reduce操作,(0~9)->0; (10~19)->10; (20~29)->20;... // 1、定义查找表 uchar table[256]; for (int i = 0; i < 256; ++i) table[i] = (char)(10 * (i / 10)); int runNum = 1; Mat I1, I2, I3, I4; // 2、遍历像素的方法 double t = (double)getTickCount(); for (int i = 0; i < runNum; ++i) { I1 = scanImageAndReduce(I, table); } t = ((double)getTickCount() - t) / getTickFrequency(); cout << "Times passed in seconds: " << t << endl; t = (double)getTickCount(); for (int i = 0; i < runNum; ++i) { I2 = scanImageAndReduceIterator(I, table); } t = ((double)getTickCount() - t) / getTickFrequency(); cout << "Times passed in seconds: " << t << endl; t = (double)getTickCount(); for (int i = 0; i < runNum; ++i) { I3 = scanImageAndReduceRandomAccess(I, table); } t = ((double)getTickCount() - t) / getTickFrequency(); cout << "Times passed in seconds: " << t << endl; t = (double)getTickCount(); for (int i = 0; i < runNum; ++i) { I4 = coreFunction(I, table); } t = ((double)getTickCount() - t) / getTickFrequency(); cout << "Times passed in seconds: " << t << endl; imshow("image", image); imshow("I1", I1); imshow("I2", I2); imshow("I3", I3); imshow("I4", I4); cout << image.at<Vec3b>(0, 0) << I1.at<Vec3b>(0, 0) << I2.at<Vec3b>(0, 0) << I3.at<Vec3b>(0, 0) << I4.at<Vec3b>(0, 0) << endl; waitKey(0); // Wait for a keystroke in the window return 0; }
当 runNum = 1 时;
Method | Time |
---|---|
Efficient Way | 0.0029501 |
Iterator | 0.005117 |
On-The-Fly RA | 0.0066011 |
LUT function | 0.0079816 |
当 runNum = 100 时;
Method | Time |
---|---|
Efficient Way | 0.285074 |
Iterator | 0.503359 |
On-The-Fly RA | 0.542601 |
LUT function | 0.202836 |
注意:cv::LUT()方法使用了多线程,当数据量很大时使用该方法速度最快;但相反的,当数据量比较小时使用该方法速度可能会变成最慢的,上述时间单位为秒。
参考:
https://docs.opencv.org/4.4.0/db/da5/tutorial_how_to_scan_images.html