opencv 4.x新增了forEach的方式遍历像素值,比传统方式略快一些。因为它本身是使用多线程并行的方法来遍历的。从opencv源码能看到这句话:
parallel_for_(cv::Range(0, LINES), PixelOperationWrapper(reinterpret_cast<Mat_<_Tp>*>(this), operation));
写了一个测试用例,把它用起来。包括单通道,三通道,浮点型等cv::Mat的遍历。
#include <iostream>
typedef cv::Point3_<uint8_t> Pixel;
void test1(cv::Mat &image)
{
//raw pointer access.
double start = (double)cv::getTickCount();
for (int r = 0; r < image.rows; ++r)
{
Pixel *ptr = image.ptr<Pixel>(r, 0);
const Pixel *ptr_end = ptr + image.cols;
for (; ptr != ptr_end; ++ptr)
{
ptr->x = 255 - ptr->x;
ptr->y = 255 - ptr->y;
ptr->z = 255 - ptr->z;
}
}
double time = (((double)cv::getTickCount() - start)) / cv::getTickFrequency();
printf(" raw pointer access time1 : %.4f seconds\n", time);
}
void test2(cv::Mat &image)
{
double start = (double)cv::getTickCount();
int w = image.cols;
int h = image.rows;
for (int row = 0; row < h; row++)
{
uchar *uc_pixel = image.data + row * image.step;
for (int col = 0; col < w; col++)
{
uc_pixel[0] = 255 - uc_pixel[0];
uc_pixel[1] = 255 - uc_pixel[1];
uc_pixel[2] = 255 - uc_pixel[2];
uc_pixel += 3;
}
}
double time = (((double)cv::getTickCount() - start)) / cv::getTickFrequency();
printf(" raw pointer access time2 : %.4f seconds\n", time);
}
void test3(cv::Mat &image) //OpenCV中C++11 lambda方式像素遍历,OpenCV4.x开始支持
{
//forEach方式的像素遍历,三通道图像
//Pixel和 position(分别指该像素的数值信息和位置信息)
//使用了x,y,z分别代表该像素点的blue, grean, red这三个通道的颜色值
//position是遍历的像素点坐标位置
//position[0]=row, position[1]=col
double start = (double)cv::getTickCount();
image.forEach<Pixel>([](Pixel &p, const int *position) -> void {
p.x = 255 - p.x;
p.y = 255 - p.y;
p.z = 255 - p.z;
});
double time = (((double)cv::getTickCount() - start)) / cv::getTickFrequency();
printf(" forEach time3 : %.4f seconds\n", time);
}
void test4(cv::Mat &image)
{
cv::Mat gray;
cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
//遍历单通道图像
//position是遍历的像素点坐标位置
//position[0]=row, position[1]=col
gray.forEach<uint8_t>([](uint8_t &p, const int *position) -> void {
p += 1;
});
}
void test5(cv::Mat &image)
{
cv::Mat gray;
cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
cv::Mat ft;
gray.convertTo(ft, CV_32FC1, 1.0 / 255.0);
//多线程并行遍历像素,需要加锁
float score = 0.8;
std::vector<cv::Point> vtPos;
std::vector<float> vtConfidences;
std::mutex mtx;
ft.forEach<float>([&vtConfidences, &vtPos, &score, &mtx](float &val, const int *position) -> void {
if (val > score)
{
mtx.lock();
vtPos.emplace_back(cv::Point(position[1], position[0])); //x,y==col,row
vtConfidences.emplace_back(val);
mtx.unlock();
}
});
std::cout << vtPos.size() << std::endl;
}
int main(int argc, char *argv[])
{
cv::Mat image = cv::imread("D:/temp/2-6-11.jpg", cv::IMREAD_COLOR);
test1(image);
test2(image);
test3(image);
test4(image);
test5(image);
return 0;
}