Aparajuli Aparajuli - 4 years ago 116
C++ Question

Accessing image pixels; comparision of MatIterator_<> and Mat::at operator

I am trying to compare the running time to scan images pixels for a grayscale image in OpenCV version 3.0. I have already gone through the OpenCV documentation how_to_scan_images

I understand that

cv::convertTo
would be the fastest at because of various optimizations. I also understand that the
cpointer
style method would be the second best. However, i am surprised at the difference between the
MatIterator
(method3) and
Mat::at
operator (method 2). The documentation how_to_scan_images mentions that the
MatIterator
should be comparatively faster than the
Mat::at<>
operator but the result that i have received is different.
Am i missing something here? Is this result expected?
At the initial stages of algorithm development i would like to use the MatIterator because of its similarity to STL iterator and is generally considered a safer way.

Any ideas on what I am doing wrong? I am using OpenCV 3.0 on Ubuntu. The output shown below is approximately consistent within +- 0.5 ms for multiple runs.

In the code below I am effectively calculating (
newImage = alpha*oldImage +beta
) and comparing the performance of the following methods

Method1: using
cv:: convertTo
, Time in ms = 0.709923

Method2: using
img.at<uchar>(row,col)
, Time in ms = 5.09625

Method3: using
MatIterator_<uchar> it
, Time in ms = 18.277

Method4:
cpointer uchar * p = img.ptr<uchar>(row)
, Time in ms = 3.49983

Method5:
cpointer uchar * src = img.data
, Time in ms = 3.28267

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/utility.hpp>
#include <iostream>
#include <string>

using namespace std;
using namespace cv;

int main(int argc, char ** argv)
{
string imgname("baboon.jpg");
if (argc>1)
imgname = argv[1];
Mat im = imread(imgname.c_str(), IMREAD_COLOR);
if (im.empty())
{ cout<<" Invalid image read, imgname =argv[1] = "<<imgname<<endl;
return -1;
}
Mat img;
cvtColor(im, img,COLOR_BGR2GRAY);
// method 1: using convertTo
Mat img1;
double alpha =1, beta =50;
double t = (double)(getTickCount());
img.convertTo(img1, img.type(),alpha, beta);
t = ((double)getTickCount() - t)/getTickFrequency();
cout<<"Method 1: using convertTo, Time elasped in ms = "<<t*1000<<endl;
namedWindow("img1", WINDOW_AUTOSIZE);
imshow("img1", img1);
// Method2: using img.at<>(row, col) index
Mat img2 = Mat::zeros(img.size(), img.type());
t = (double)(getTickCount());
for (int row=0; row<img.rows; row++)
{ for (int col =0; col<img.cols; col++)
{ img2.at<uchar>(row,col) = (saturate_cast<uchar>) (alpha*img.at<uchar> (row,col) + beta);
}
}
t = ((double)getTickCount() - t)/getTickFrequency();
cout<<"Method2: using img.at<uchar>(row,col), Time in ms = "<<t*1000<<endl;

//Method3: using Matiterator for each image point
Mat img3 = Mat::zeros(img.size(),img.type());
t = (double)(getTickCount());
MatIterator_<uchar> it, itDest, end;
itDest= img3.begin<uchar>(); end =img.end<uchar>();
for (it = img.begin<uchar>(); it!= end; it++,itDest++)
{ *itDest = (saturate_cast<uchar>)((*it)*alpha + beta);
}
t = ((double)getTickCount() - t)/getTickFrequency();
cout<<"Method3: using MatIterator_<uchar> it, Time in ms = "<<t*1000<<endl;
//Method4: using c-style pointer, char * p = img.ptr<uchar>(rowNum)
Mat img4 = Mat::zeros(img.size(), img.type());
t = (double)(getTickCount());
for (int row =0; row<img.rows; row++)
{ uchar * srcPtr = img.ptr<uchar>(row);
uchar * destPtr = img4.ptr<uchar>(row);
for (int col =0; col<img.cols; col++)
{ destPtr[col] = (saturate_cast<uchar>) (alpha* srcPtr[col] + beta);
}
}
t = ((double)getTickCount() - t)/getTickFrequency();
cout<<"Method4: cpointer uchar * p = img.ptr<uchar>(row), Time in ms = "<<t*1000<<endl;

// method 5: using the address given by img.data() and iterating untill the end
Mat img5 = Mat::zeros(img.size(), img.type());
t = (double)(getTickCount());
uchar * src = img.data;
uchar * dest =img5.data;
for (int i=0; i<img.rows*img.cols; i++)
*(dest++) = (saturate_cast<uchar>)(alpha * (*(src++)) + beta);
t = ((double)getTickCount() - t)/getTickFrequency();
cout<<"Method5: cpointer uchar * src = img.data, Time in ms = "<<t*1000<<endl;
return 0;

}

Answer Source

After digging on my own here is my conclusion : Image scanning methods comparison based upon performance(highest to lowest)

  1. Use library function whenever possible.
  2. Prefer C pointer for faster scan(one can use img.data() for few ms gain but be careful!)
  3. img.at<> is the next best
  4. MatIterator is the least fast.

Only in release mode 'Mat::at' is faster than 'MatIterator'. MatIterator brings the safety factor with additional checks. However, in debug mode 'MatIterator' is faster than "Mat::at" operator

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download