Cycle through pixels with opencv
Asked Answered
C

7

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?

Colic answered 21/12, 2010 at 22:49 Comment(4)
Possible duplicate: #998929Pulque
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]; }Colic
#3859722Dagenham
#3852104Dagenham
S
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.

Senarmontite answered 22/12, 2010 at 0:9 Comment(2)
Thank you for the link! Looks similar to QImage::scanLine, just what I wanted.Bathtub
The page you are linking is not available anymore.Jankowski
U
55

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.

Ululate answered 22/12, 2010 at 0:19 Comment(8)
How is it that you get values that are non-integers for the image?Ululate
img.at<uchar>(i,j)[0] - this isn't going to work :) the [0] has to goSenarmontite
I got to this question and it solved a problem I had, but now I have the question, why does ::at<uchar> work? why doesnt it work with the at alone? like at(0,0)?Schechinger
Because ::at<T> is a template method without a default typename?Ululate
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...Fireplug
@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?Mccune
I would not know unless you put your code. Perhaps make another question here on StackOverflow?Ululate
if I want to use this to simply copy pixels, it does not workDanseuse
G
20

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.

Glazier answered 26/8, 2015 at 16:27 Comment(2)
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!!!Danseuse
Please update the tutorial docs.opencv.org/3.4/db/da5/tutorial_how_to_scan_images.html to describe this as well!Littlefield
S
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.

Senarmontite answered 22/12, 2010 at 0:9 Comment(2)
Thank you for the link! Looks similar to QImage::scanLine, just what I wanted.Bathtub
The page you are linking is not available anymore.Jankowski
P
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.

Pika answered 10/2, 2019 at 10:11 Comment(0)
G
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;
Gleeson answered 28/6, 2018 at 20:43 Comment(0)
D
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).

Dentation answered 27/5, 2015 at 8:11 Comment(0)
T
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;
    }
    }
}
Timorous answered 21/9, 2017 at 4:52 Comment(1)
Good point, this is what I was looking for. No need for row/col if you treat pixels independently!Thermotaxis

© 2022 - 2024 — McMap. All rights reserved.