54

I'm trying to set a new color value to some pixel into a cv::Mat image my code is below:

    Mat image = img;
    for(int y=0;y<img.rows;y++)
    {
        for(int x=0;x<img.cols;x++)
        {
        Vec3b color = image.at<Vec3b>(Point(x,y));
        if(color[0] > 150 && color[1] > 150 && color[2] > 150)
        {
            color[0] = 0;
            color[1] = 0;
            color[2] = 0;
            cout << "Pixel >200 :" << x << "," << y << endl;
        }
        else
        {
            color.val[0] = 255;
            color.val[1] = 255;
            color.val[2] = 255;
        }
    }
    imwrite("../images/imgopti"+to_string(i)+".tiff",image);

It seems to get the good pixel in output (with cout) however in the output image (with imwrite) the pixel concerned aren't modified. I have already tried using color.val[0].. I still can't figure out why the pixel colors in the output image dont change. thanks

Howli
  • 12,291
  • 19
  • 47
  • 72
grll
  • 1,047
  • 2
  • 10
  • 18

3 Answers3

89

You did everything except copying the new pixel value back to the image.

This line takes a copy of the pixel into a local variable:

Vec3b color = image.at<Vec3b>(Point(x,y));

So, after changing color as you require, just set it back like this:

image.at<Vec3b>(Point(x,y)) = color;

So, in full, something like this:

Mat image = img;
for(int y=0;y<img.rows;y++)
{
    for(int x=0;x<img.cols;x++)
    {
        // get pixel
        Vec3b & color = image.at<Vec3b>(y,x);

        // ... do something to the color ....
        color[0] = 13;
        color[1] = 13;
        color[2] = 13;

        // set pixel
        //image.at<Vec3b>(Point(x,y)) = color;
        //if you copy value
    }
}
Arsen Khachaturyan
  • 7,904
  • 4
  • 42
  • 42
Roger Rowland
  • 25,885
  • 11
  • 72
  • 113
21

just use a reference:

Vec3b & color = image.at<Vec3b>(y,x);
color[2] = 13;
berak
  • 39,159
  • 9
  • 91
  • 89
  • 2
    because the problem was about a color Mat. in the uchar case, ofc. use `uchar& c = image.at(y,x)` – berak Aug 02 '14 at 14:54
17

I would not use .at for performance reasons.

Define a struct:

//#pragma pack(push, 2) //not useful (see comments below)
struct BGR {
    uchar blue;
    uchar green;
    uchar red;  };

And then use it like this on your cv::Mat image:

BGR& bgr = image.ptr<BGR>(y)[x];

image.ptr(y) gives you a pointer to the scanline y. And iterate through the pixels with loops of x and y

seht97
  • 67
  • 6
Flocke
  • 1,004
  • 17
  • 23
  • What is the sense of setting `pack` value to 2 if `sizeof(uchar)` is 1? – Antony Hatchkins Aug 02 '14 at 16:01
  • 1
    The compiler will add padding Bytes to a struct. The setting of pack changes those paddings so that the data is aligned in another way.Edit: See this post http://stackoverflow.com/questions/3318410/pragma-pack-effect – Flocke Aug 03 '14 at 07:34
  • 3
    `pragma` has no effect on a struct made solely from `uchar`s. Read a nice explanation [here](http://publib.boulder.ibm.com/infocenter/compbgpl/v9v111/index.jsp?topic=/com.ibm.xlcpp9.bg.doc/compiler_ref/pragma_pack.htm) for details. – Antony Hatchkins Aug 03 '14 at 15:16
  • Thx good link. Seem the pragma really is useless in this case :) thx! – Flocke Aug 03 '14 at 16:57
  • You're welcome :) And nice answer: less magic, more efficient. – Antony Hatchkins Aug 03 '14 at 17:50
  • You actually want to call this struct BGR, or similiar, since by default OpenCV stores the bands in BGR format. – trieck Sep 13 '20 at 21:49
  • Yes, the struct-name is in the wrong order. No biggy in my opinion though... but yes, you are correct :) – Flocke Sep 14 '20 at 09:46