37

How would I be able to cycle through an image using opencv as if it were a 2d array to get the rgb values of each pixel? Also, would a mat be preferable over an iplimage for this operation?

a sandwhich
  • 4,352
  • 12
  • 41
  • 62
  • Possible duplicate: http://stackoverflow.com/questions/998429/opencv-accessing-and-taking-the-square-root-of-pixels – Eugen Constantin Dinca Dec 21 '10 at 23:26
  • I am just going to use this little function I found. float pixval32f( IplImage* img, int r, int c ) { return ( (float*)(img->imageData + img->widthStep*r) )[c]; } – a sandwhich Dec 22 '10 at 01:06
  • http://stackoverflow.com/questions/3859222/c-negative-rgb-values-of-pixels-using-opencv/3860920#3860920 – karlphillip Dec 22 '10 at 12:59
  • http://stackoverflow.com/questions/3851604/c-access-violation-using-opencv-to-get-rgb-value-of-pixel/3851670#3851670 – karlphillip Dec 22 '10 at 13:00

7 Answers7

54

cv::Mat is preferred over IplImage because it simplifies your code

cv::Mat img = cv::imread("lenna.png");
for(int i=0; i<img.rows; i++)
    for(int j=0; j<img.cols; j++) 
        // You can now access the pixel value with cv::Vec3b
        std::cout << img.at<cv::Vec3b>(i,j)[0] << " " << img.at<cv::Vec3b>(i,j)[1] << " " << img.at<cv::Vec3b>(i,j)[2] << std::endl;

This assumes that you need to use the RGB values together. If you don't, you can uses cv::split to get each channel separately. See etarion's answer for the link with example.

Also, in my cases, you simply need the image in gray-scale. Then, you can load the image in grayscale and access it as an array of uchar.

cv::Mat img = cv::imread("lenna.png",0);
for(int i=0; i<img.rows; i++)
    for(int j=0; j<img.cols; j++)
        std::cout << img.at<uchar>(i,j) << std::endl;

UPDATE: Using split to get the 3 channels

cv::Mat img = cv::imread("lenna.png");
std::vector<cv::Mat> three_channels = cv::split(img);

// Now I can access each channel separately
for(int i=0; i<img.rows; i++)
    for(int j=0; j<img.cols; j++)
        std::cout << three_channels[0].at<uchar>(i,j) << " " << three_channels[1].at<uchar>(i,j) << " " << three_channels[2].at<uchar>(i,j) << std::endl;

// Similarly for the other two channels

UPDATE: Thanks to entarion for spotting the error I introduced when copying and pasting from the cv::Vec3b example.

Dat Chu
  • 10,822
  • 13
  • 58
  • 82
  • 1
    How is it that you get values that are non-integers for the image? – Dat Chu Dec 22 '10 at 04:56
  • 1
    `img.at(i,j)[0]` - this isn't going to work :) the `[0]` has to go – etarion Dec 22 '10 at 09:48
  • I got to this question and it solved a problem I had, but now I have the question, why does ::at work? why doesnt it work with the at alone? like at(0,0)? – lesolorzanov Nov 28 '11 at 03:55
  • Because ::at is a template method without a default typename? – Dat Chu Dec 01 '11 at 05:01
  • This is one of the most useful posts for OpenCV beginners ever. Would be perfect if it had some additional info on how to cycle trough that same Mat as if it was a 1D matrix. But I guess that was not the question... – Void Sep 17 '13 at 12:06
  • @DatChu you mention getting non-integer values for the image. When I run the first code example above, I get output like `+ > H`. Do you know any reason for this? – Linell Oct 20 '13 at 20:19
  • I would not know unless you put your code. Perhaps make another question here on StackOverflow? – Dat Chu Oct 21 '13 at 16:41
  • if I want to use this to simply copy pixels, it does not work – Arwed Mett Nov 22 '17 at 22:27
19

Since OpenCV 3.0, there are official and fastest way to run function all over the pixel in cv::Mat.

void cv::Mat::forEach (const Functor& operation)

If you use this function, operation is runs on multi core automatically.

Disclosure : I'm contributor of this feature.

user3188838
  • 362
  • 4
  • 8
  • if this would only solve the problem, the user of the library actually would rather have not to deal with the types. Maybe I don't get it, but somehow it is really tedious to "simply" read out pixels or modify them!!! – Arwed Mett Nov 22 '17 at 22:37
  • 2
    Please update the tutorial https://docs.opencv.org/3.4/db/da5/tutorial_how_to_scan_images.html to describe this as well! – gokul_uf Jul 31 '18 at 15:23
11

If you use C++, use the C++ interface of opencv and then you can access the members via http://docs.opencv.org/2.4/doc/tutorials/core/how_to_scan_images/how_to_scan_images.html#the-efficient-way or using cv::Mat::at(), for example.

etarion
  • 16,935
  • 4
  • 43
  • 66
9

This is an old question but needs to get updated since opencv is being actively developed. Recently, OpenCV has introduced parallel_for_ which complies with c++11 lambda functions. Here is the example

parallel_for_(Range(0 , img.rows * img.cols), [&](const Range& range){
    for(int r = range.start; r<range.end; r++ )
    {
         int i = r / img.cols;
         int j = r % img.cols;
        img.ptr<uchar>(i)[j] = doSomethingWithPixel(img.at<uchar>(i,j));
    }
});

This is mention-worthy that this method uses the CPU cores in modern computer architectures.

Davood Falahati
  • 1,474
  • 16
  • 34
6

Since OpenCV 3.3 (see changelog) it is also possible to use C++11 style for loops:

// Example 1
Mat_<Vec3b> img = imread("lena.jpg");
for( auto& pixel: img ) {
    pixel[0] = gamma_lut[pixel[0]];
    pixel[1] = gamma_lut[pixel[1]];
    pixel[2] = gamma_lut[pixel[2]];
}

// Example 2
Mat_<float> img2 = imread("float_image.exr", cv::IMREAD_UNCHANGED);
for(auto& p : img2) p *= 2;
Martin R.
  • 1,554
  • 17
  • 16
3

The docs show a well written comparison of different ways to iterate over a Mat image here.

The fastest way is to use C style pointers. Here is the code copied from the docs:

Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
// accept only char type matrices
CV_Assert(I.depth() != sizeof(uchar));

int channels = I.channels();

int nRows = I.rows;
int nCols = I.cols * channels;

if (I.isContinuous())
{
    nCols *= nRows;
    nRows = 1;
}

int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
    p = I.ptr<uchar>(i);
    for ( j = 0; j < nCols; ++j)
    {
        p[j] = table[p[j]];
    }
}
return I;
}

Accessing the elements with the at is quite slow.

Note that if your operation can be performed using a lookup table, the built in function LUT is by far the fastest (also described in the docs).

vipers36
  • 300
  • 1
  • 11
1

If you want to modify RGB pixels one by one, the example below will help!

void LoopPixels(cv::Mat &img) {
    // Accept only char type matrices
    CV_Assert(img.depth() == CV_8U);

    // Get the channel count (3 = rgb, 4 = rgba, etc.)
    const int channels = img.channels();
    switch (channels) {
    case 1:
    {
        // Single colour
        cv::MatIterator_<uchar> it, end;
        for (it = img.begin<uchar>(), end = img.end<uchar>(); it != end; ++it)
            *it = 255;
        break;
    }
    case 3:
    {
        // RGB Color
        cv::MatIterator_<cv::Vec3b> it, end;
        for (it = img.begin<cv::Vec3b>(), end = img.end<cv::Vec3b>(); it != end; ++it) {
            uchar &r = (*it)[2];
            uchar &g = (*it)[1];
            uchar &b = (*it)[0];
            // Modify r, g, b values
            // E.g. r = 255; g = 0; b = 0;
        }
        break;
    }
    }
}
Ari Seyhun
  • 11,506
  • 16
  • 62
  • 109
  • Good point, this is what I was looking for. No need for row/col if you treat pixels independently! – Vlad Nov 28 '18 at 20:22