Reputation: 5659
I have read an image in Mat
format.
Mat image = imread("image.png", 1);
I declare a pointer to its data using
unsigned char *ptr_source = image.data
Now, I want to access the value of R,G and B values at each pixel in a for loop
. I already know the method to do it with img.at<Veb3b>(i,j)
or similar things but now, I have to do it using a pointer of unsigned char
type.
uchar R_value = ptr_source[ i*?? + ??? ];
uchar G_value = ptr_source[ i*?? + ??? ];
uchar B_value = ptr_source[ i*?? + ??? ];
IMPORTANT: Some people here have mentioned to use the following:
unsigned char *input = (unsigned char*)(img.data);
for(int j = 0;j < img.rows;j++){
for(int i = 0;i < img.cols;i++){
unsigned char b = input[img.step * j + i ] ;
unsigned char g = input[img.step * j + i + 1];
unsigned char r = input[img.step * j + i + 2];
}
}
which makes sense to me as per the openCV docs but unfortunately it is not working in my case. The other method posted at SO says to use the following:
uchar b = frame.data[frame.channels()*(frame.cols*y + x) + 0];
uchar g = frame.data[frame.channels()*(frame.cols*y + x) + 1];
uchar r = frame.data[frame.channels()*(frame.cols*y + x) + 2];
Basic Question: Though, it seems to be working but I do not understand it logically. Why do we need to multiply (frame.cols*y + x)
with frame.channels()
??
Upvotes: 1
Views: 2718
Reputation: 17275
The cv::Mat::channels()
method returns the number of channels in an image.
In a 8UC3
three-channel color image, channels()
returns 3, and the pixels are stored in consecutive byte-triplets: BGRBGRBGRBGRBGR...
.
To access pixel (x,y)
given a unsigned char* ptr_source
pointer, you need to calculate the pixel offset. The image width is frame.cols
. Each pixel is channels() == 3
bytes, so the pixel's unsiged char*
offset will be ptr_source + frame.channels()*(frame.cols*y + x)
. This unsigned char*
would usually be the blue channel with the following 2 char
s the green and red.
For example, given a 3x4 image, the pixels in memory would look like this (spaces for clarity only):
r\c 0 1 2
0 BGR BGR BGR
1 BGR BGR BGR
2 BGR>BGR<BGR
3 BGR BGR BGR
So if you count bytes you'll see that the blue channel byte of pixel (1,2)
is exactly at byte offset 3*(2*3+1) = 21
It is actually advisable to use img.step
instead of the raw computation since some images have padding at the end of each pixel row so that it is not always true that img.step[0] == img.channels()*img.cols
.
In this case you should use ptr_source[img.step[0]*y + img.channels()*x]
.
Additionally, your question assumes that the pixel depth is 8U
which may not be correct for all images. If it is not, you will need to multiply everything by the depth (bytes per pixel) as well.
And this is essentially what cv::Mat:at<>
does...
Upvotes: 2