Reputation: 31
I use OpenCV (C++) Mat for my matrix and want to acces single Mat elements as fast as possible. From OpenCV tutorial, I found code for efficient acces:
for( i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i);
for ( j = 0; j < nCols; ++j)
{
p[j] = table[p[j]];
}
}
For my problem, I need to access a Mat element and its neighbours (i-1,j-1) for a calculation. How can I adapt the given code to acces a single mat element AND its surrounding elements? Since speed matters, I want to avoid Mat.at<>()
.
What is the most efficient way to acces a Mat value and its neighbour values?
Upvotes: 1
Views: 4467
Reputation: 1468
For any future readers: Instead of reading the answers here, please read this blog post https://www.learnopencv.com/parallel-pixel-access-in-opencv-using-foreach/ for a benchmark-based analysis of this functionality, as some of the answers are a bit off the bat.
From that post you can see that the fastest way to access pixels is using the forEach C++ Mat function. If you want the neighborhood it depends of the size; if you're looking for the usual squared 3x3 neighborhood, use pointers like this:
Mat img = Mat(100,100,CV_8U, Scalar(124)); // sample mat
uchar *up, *row, *down; // Pointers to rows
uchar n[9]; // neighborhood
for (int y = 1 ; y < (img.rows - 1) ; y++) {
up = img.ptr(y - 1);
row = img.ptr(y);
down = img.ptr(y + 1);
for (int x = 1 ; x < (img.cols - 1) ; x++) {
// Examples of how to access any pixel in the 8-connected neighborhood
n[0] = up[x - 1];
n[1] = up[x];
n[2] = up[x + 1];
n[3] = row[x - 1];
n[4] = row[x];
n[5] = row[x + 1];
n[6] = down[x - 1];
n[7] = down[x];
n[8] = down[x + 1];
}
}
This code can still be optimized but the idea of using row pointers is what I was trying to convey; this is just a bit faster than using the .at() function and you might have to do benchmarking to notice the difference (in versions of OpenCV 3+). You might want to use .at() before deciding to optimize pixel access.
Upvotes: 0
Reputation: 4074
You can directly refers to Mat::data:
template<class T, int N>
T GetPixel(const cv::Mat &img, int x, int y) {
int k = (y * img.cols + x) * N;
T pixel;
for(int i=0;i<N;i++)
pixel[i] = *(img.data + k + i);
return pixel;
}
template<class T,int N>
void SetPixel(const cv::Mat &img, int x, int y, T t) {
int k = (y * img.cols + x) * N;
for(int i=0;i<N;i++)
*(img.data + k + i) = t[i];
}
template<>
unsigned char GetPixel<unsigned char, 1>(const cv::Mat &img, int x, int y) {
return *(img.data + y * img.cols + x);
}
template<>
void SetPixel<unsigned char, 1>(const cv::Mat &img, int x, int y, unsigned char p) {
*(img.data + y * img.cols + x) = p;
}
int main() {
unsigned char r,g,b;
int channels = 3;
Mat img = Mat::zeros(256,256, CV_8UC3);
for(int x=0;x<img.cols;x+=2)
for(int y=0;y<img.rows;y+=2)
SetPixel<cv::Vec3b, 3>(img, x, y, cv::Vec3b(255,255,255));
Mat imgGray = Mat::zeros(256,256, CV_8UC1);
for(int x=0;x<imgGray.cols;x+=4)
for(int y=0;y<imgGray.rows;y+=4)
SetPixel<unsigned char, 1>(imgGray, x, y, (unsigned char)255);
imwrite("out.jpg", img);
imwrite("outGray.jpg", imgGray);
return 0;
}
That is pretty fast I think.
out.jpg:
outGray.jpg:
Upvotes: 0
Reputation: 50667
The pixel and its neighbor pixels can be formed a cv::Rect
, then you can simply use:
cv::Mat mat = ...;
cv::Rect roi= ...; // define it properly based on the neighbors defination
cv::Mat sub_mat = mat(roi);
In case your neighbors definition is not regular, i.e. they cannot form a rectangle area, use mask instead. Check out here for examples.
Upvotes: 1