36

Maybe I'm not looking hard enough, but everything seems to want me to use an array. Thus, how do I get the channel value for a particular pixel for foo if foo is something like Mat foo = imread("bar.png")?

Antonio
  • 17,405
  • 10
  • 78
  • 178
HRÓÐÓLFR
  • 5,494
  • 5
  • 30
  • 32
  • Maybe this will help you : http://opencv.itseez.com/modules/core/doc/old_basic_structures.html?highlight=get2d#CvScalar cvGet2D(const CvArr* arr, int idx0, int idx1) – Adrian Oct 26 '11 at 05:56
  • It seems like it doesn't let me post the proper link, just go to the Get?D methods. – Adrian Oct 26 '11 at 06:02
  • is there a similar method for cv::Mat as opposed to CvArr*? – HRÓÐÓLFR Oct 26 '11 at 06:18
  • I think this could be something similar for Mat : http://opencv.itseez.com/modules/core/doc/basic_structures.html#mat-at But don't know exactly how you would get the channel value as it doesn't return a CvScalar. – Adrian Oct 26 '11 at 06:38

3 Answers3

104

Assuming the type is CV_8UC3 you would do this:

for(int i = 0; i < foo.rows; i++)
{
    for(int j = 0; j < foo.cols; j++)
    {
        Vec3b bgrPixel = foo.at<Vec3b>(i, j);

        // do something with BGR values...
    }
}

Here is the documentation for Vec3b. Hope that helps! Also, don't forget OpenCV stores things internally as BGR not RGB.

EDIT :
For performance reasons, you may want to use direct access to the data buffer in order to process the pixel values:

Here is how you might go about this:

uint8_t* pixelPtr = (uint8_t*)foo.data;
int cn = foo.channels();
Scalar_<uint8_t> bgrPixel;

for(int i = 0; i < foo.rows; i++)
{
    for(int j = 0; j < foo.cols; j++)
    {
        bgrPixel.val[0] = pixelPtr[i*foo.cols*cn + j*cn + 0]; // B
        bgrPixel.val[1] = pixelPtr[i*foo.cols*cn + j*cn + 1]; // G
        bgrPixel.val[2] = pixelPtr[i*foo.cols*cn + j*cn + 2]; // R

        // do something with BGR values...
    }
}

Or alternatively:

int cn = foo.channels();
Scalar_<uint8_t> bgrPixel;

for(int i = 0; i < foo.rows; i++)
{
    uint8_t* rowPtr = foo.row(i);
    for(int j = 0; j < foo.cols; j++)
    {
        bgrPixel.val[0] = rowPtr[j*cn + 0]; // B
        bgrPixel.val[1] = rowPtr[j*cn + 1]; // G
        bgrPixel.val[2] = rowPtr[j*cn + 2]; // R

        // do something with BGR values...
    }
}
Antonio
  • 17,405
  • 10
  • 78
  • 178
mevatron
  • 13,381
  • 3
  • 51
  • 68
  • and what about if my original matrix is CV_32FC3? – nkint Mar 21 '12 at 18:27
  • because using Vec3f and coutting them i'm getting strange result like: 327880373263237315260312545394688.0000 1058748366848.0000 -0.0000 – nkint Mar 21 '12 at 18:42
  • i've done a post: http://stackoverflow.com/questions/9811420/opencv-accessing-color-element-of-cv-32fc3-bgr-cvmat – nkint Mar 21 '12 at 19:20
  • @mevatron, I notice you don't have pixel size in your access function, as in, bgrPixel.val[0] = pixelPtr[i*foo.cols*4 + j*4 + 0]; for bgra data, is that intentional? – Mark Essel Sep 23 '12 at 16:54
  • 1
    @MarkEssel Good catch! I had taken some of my grayscale processing code, and didn't update it. I updated the code to utilize the number of channels in the matrix. Assuming there are three like the OP needed. – mevatron Sep 24 '12 at 13:56
  • I got your back, way back :D. Good coding – Mark Essel Sep 25 '12 at 20:29
  • If I have a grayscale image how can I access the pixel values? Is this correct: data[step * y + x]; @ (dataB1 + y*stepB1)[channelB1*x]? – Mzk Jan 16 '13 at 15:43
  • Thanks! This was helpful. The openCV docs aren't thourough as a newcomer could want. I think in your second example j += cn should just be j++, otherwise it doesn't hit every element in the array. Also, still as a newbie, is there a reason to declare bgrPixel inside the nested loop rather than outside? – user1738934 Jun 22 '13 at 15:06
  • I agree with user1738934, j+=cn should be j++. What do you say @mevatron – Luniam Jun 12 '14 at 16:16
  • @Luniam Thanks for catching that. Code has been corrected. – mevatron Jun 12 '14 at 16:28
15

The below code works for me, for both accessing and changing a pixel value.

For accessing pixel's channel value :

for (int i = 0; i < image.cols; i++) {
    for (int j = 0; j < image.rows; j++) {
        Vec3b intensity = image.at<Vec3b>(j, i);
        for(int k = 0; k < image.channels(); k++) {
            uchar col = intensity.val[k]; 
        }   
    }
}

For changing a pixel value of a channel :

uchar pixValue;
for (int i = 0; i < image.cols; i++) {
    for (int j = 0; j < image.rows; j++) {
        Vec3b &intensity = image.at<Vec3b>(j, i);
        for(int k = 0; k < image.channels(); k++) {
            // calculate pixValue
            intensity.val[k] = pixValue;
        }
     }
}

`

Source : Accessing pixel value

Yoda
  • 422
  • 3
  • 13
  • I used your code for changing the pixel value and tried to display it. Somehow only part of the image changes, the rest is still unchanged. Any reasons for that? The code is: intensity.val[k]=(i+j)%256; – MuneshSingh Oct 22 '18 at 06:34
  • On second thought the above issue only comes when using PNG image format. For JPEG format, the entire image pixel values are changing correctly. – MuneshSingh Oct 22 '18 at 07:00
  • This does not look correct. If `image.channels()` is anything different than 3, then `Vec3b` is not the right type to store the matrix entries. – DarioP Jun 16 '20 at 19:10
1

The pixels array is stored in the "data" attribute of cv::Mat. Let's suppose that we have a Mat matrix where each pixel has 3 bytes (CV_8UC3).

For this example, let's draw a RED pixel at position 100x50.

Mat foo;
int x=100, y=50;

Solution 1:

Create a macro function that obtains the pixel from the array.

#define PIXEL(frame, W, x, y) (frame+(y)*3*(W)+(x)*3)
//...
unsigned char * p = PIXEL(foo.data, foo.rols, x, y);
p[0] = 0;   // B
p[1] = 0;   // G
p[2] = 255; // R

Solution 2:

Get's the pixel using the method ptr.

unsigned char * p = foo.ptr(y, x); // Y first, X after
p[0] = 0;   // B
p[1] = 0;   // G
p[2] = 255; // R
Derzu
  • 6,253
  • 1
  • 49
  • 56