learn opencv-使用forEach进行并行像素访问
来源:互联网 发布:豆豆软件 编辑:程序博客网 时间:2024/05/01 05:34
参考:https://github.com/spmallick/learnopencv
OpenCV中使用forEach进行并行像素访问
在本教程中,我们将比较Mat类的forEach方法的性能和访问和转换OpenCV中像素值的其他方式。 我们将展示如何使用at方法甚至高效地使用指针算法,forEach比使用at方法快得多。
OpenCV中有隐藏的宝石,有时不是很知名。 其中一个隐藏的宝石是Mat类的forEach方法,它利用机器上的所有内核在每个像素上应用任何函数。
让我们先定义一个函数complexThreshold。 它采用一个RGB像素值并对其应用一个复杂的阈值。
// Define a pixel typedef Point3_<uint8_t> Pixel;// A complicated threshold is defined so // a non-trivial amount of computation // is done at each pixel. void complicatedThreshold(Pixel &pixel){ if (pow(double(pixel.x)/10,2.5) > 100) { pixel.x = 255; pixel.y = 255; pixel.z = 255; } else { pixel.x = 0; pixel.y = 0; pixel.z = 0; }}
与简单的阈值相比,这个函数在计算上要重得多。 这样,我们不仅仅是测试像素访问时间,而且每个像素操作的计算量都很大时,forEach如何使用所有内核。
接下来,我们将通过四种不同的方式将这个函数应用到图像中的每个像素,并检查相关的性能。
方法1:使用at方法的朴素像素访问
Mat类有一个方便的方法来访问图像中位置(行,列)的像素。 以下代码使用at方法来访问每个像素并将复杂的阈值应用于它。
// Naive pixel access// Loop over all rowsfor (int r = 0; r < image.rows; r++){ // Loop over all columns for ( int c = 0; c < image.cols; c++) { // Obtain pixel at (r, c) Pixel pixel = image.at<Pixel>(r, c); // Apply complicatedTreshold complicatedThreshold(pixel); // Put result back image.at<Pixel>(r, c) = pixel; }}
上面的方法被认为是低效的,因为每次我们调用at方法时,内存中像素的位置正在被计算。 这涉及乘法操作。 不使用像素位于连续的存储器块中的事实。
方法2:使用指针算法进行像素访问
在OpenCV中,一行中的所有像素都存储在一个连续的内存块中。 如果使用create创建了Mat对象,则所有像素都存储在一个连续的内存块中。 由于我们正在从磁盘读取图像,imread使用create方法,因此我们可以简单地使用不需要乘法的指针运算来遍历所有像素。
代码如下所示。
// Using pointer arithmetic// Get pointer to first pixelPixel* pixel = image1.ptr<Pixel>(0,0);// Mat objects created using the create method are stored// in one continous memory block.const Pixel* endPixel = pixel + image1.cols * image1.rows;// Loop over all pixelsfor (; pixel != endPixel; pixel++){ complicatedThreshold(*pixel);}
方法3:使用forEach
Mat类的forEach方法接受一个函数操作符。 用法是
void cv::Mat::forEach (const Functor &operation)
了解上述用法的最简单的方法是通过下面的示例。 我们定义了一个用于forEach的函数对象(Operator)。
// Parallel execution with function object.struct Operator{ void operator ()(Pixel &pixel, const int * position) const { // Perform a simple threshold operation complicatedThreshold(pixel); }};
调用forEach很简单,只需要一行代码即可完成
// Call forEachimage2.forEach<Pixel>(Operator());
方法4:在C ++ 11 Lambda中使用forEach
image3.forEach<Pixel>( [](Pixel &pixel, const int * position) -> void { complicatedThreshold(pixel); });
比较forEach的性能
复杂阈值函数连续五次应用于大小为9000 x 6750的大图像的所有像素。 实验中使用的2.5 GHz Intel Core i7处理器有四个内核。 以下时间已经获得。 请注意,使用forEach比使用Naive Pixel Access或Pointer Arithmetic方法快五倍。
我已经在OpenCV中编写了十多年的代码,每当我必须编写访问像素的优化代码时,我都会使用指针算法而不是naive 的方法。 不过,在写这篇博文的时候,我惊讶地发现,即使是大图片,这两种方法之间似乎也没有什么区别。
完整代码:
// Include OpenCV header#include <opencv2/opencv.hpp>// Use cv and std namespacesusing namespace cv;using namespace std;// Define a pixeltypedef Point3_<uint8_t> Pixel;// tic is called to start timervoid tic(double &t){ t = (double)getTickCount();}// toc is called to end timerdouble toc(double &t){ return ((double)getTickCount() - t) / getTickFrequency();}void complicatedThreshold(Pixel &pixel){ if (pow(double(pixel.x) / 10, 2.5) > 100) { pixel.x = 255; pixel.y = 255; pixel.z = 255; } else { pixel.x = 0; pixel.y = 0; pixel.z = 0; }}// Parallel execution with function object.struct Operator{ void operator ()(Pixel &pixel, const int * position) const { // Perform a simple threshold operation complicatedThreshold(pixel); }};int main(int argc, char** argv){ // Read image Mat image = imread("butterfly.jpg"); // Scale image 30x resize(image, image, Size(), 30, 30); // Print image size cout << "Image size " << image.size() << endl; // Number of trials int numTrials = 5; // Print number of trials cout << "Number of trials : " << numTrials << endl; // Make two copies Mat image1 = image.clone(); Mat image2 = image.clone(); Mat image3 = image.clone(); // Start timer double t; tic(t); for (int n = 0; n < numTrials; n++) { // Naive pixel access // Loop over all rows for (int r = 0; r < image.rows; r++) { // Loop over all columns for (int c = 0; c < image.cols; c++) { // Obtain pixel at (r, c) Pixel pixel = image.at<Pixel>(r, c); // Apply complicatedTreshold complicatedThreshold(pixel); // Put result back image.at<Pixel>(r, c) = pixel; } } } cout << "Naive way: " << toc(t) << endl; // Start timer tic(t); // image1 is guaranteed to be continous, but // if you are curious uncomment the line below // cout << "Image 1 is continous : " << image1.isContinuous() << endl; for (int n = 0; n < numTrials; n++) { // Get pointer to first pixel Pixel* pixel = image1.ptr<Pixel>(0, 0); // Mat objects created using the create method are stored // in one continous memory block. const Pixel* endPixel = pixel + image1.cols * image1.rows; // Loop over all pixels for (; pixel != endPixel; pixel++) { complicatedThreshold(*pixel); } } cout << "Pointer Arithmetic " << toc(t) << endl; tic(t); for (int n = 0; n < numTrials; n++) { image2.forEach<Pixel>(Operator()); } cout << "forEach : " << toc(t) << endl;#if __cplusplus >= 201103L || (__cplusplus < 200000 && __cplusplus > 199711L) tic(t); for (int n = 0; n < numTrials; n++) { // Parallel execution using C++11 lambda. image3.forEach<Pixel> ( [](Pixel &pixel, const int * position) -> void { complicatedThreshold(pixel); } ); } cout << "forEach C++11 : " << toc(t) << endl;#endif return EXIT_SUCCESS;}
- learn opencv-使用forEach进行并行像素访问
- Opencv并行访问图像像素
- opencv 使用Mat 矩阵进行图像的降采样,像素的访问方式
- 如何使用 opencv 访问图像像素
- learn opencv-使用OpenCV进行Alpha混合(C ++ / Python)
- opencv访问图像像素
- 用迭代器访问像素OPENCV
- opencv访问图像像素
- Opencv访问图像像素
- opencv:访问像素
- OpenCV使用迭代器对像素进行快速操作
- OpenCV 2 访问图像像素
- OpenCV之像素访问优化
- OpenCV 访问图像像素点
- Opencv访问像素点方法
- Opencv用指针访问像素
- opencv 访问每个像素点
- 用指针访问像素OPENCV
- 获取当前文档中的标高
- 恒德智能家居系统大行业解决方案
- Linux常用文本比对命令——diff
- 常考的Ajax面试题
- android:使用audiotrack 类播放wav文件
- learn opencv-使用forEach进行并行像素访问
- UDS诊断服务中网络层对应用层数据的封装(15765.2)
- CSDN-markdown编辑器
- jdk1.5新特性
- 题目:1009. 说反话 (20)(c++迭代器实现)
- c++文件读写之getline
- MySQL的timestamp类型自动更新问题
- 【胖张】Nginx 实现前后端分离部署(简单)
- NOIP赛前要注意的地方