Cycle through pixels with opencv

后端 未结 7 2192
情深已故
情深已故 2020-12-07 17:46

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

相关标签:
7条回答
  • 2020-12-07 18:26

    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;
        }
        }
    }
    
    0 讨论(0)
  • 2020-12-07 18:33

    This is an old question but needs to get updated since opencv is being actively developed. Recently, OpenCV has introduce 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.

    0 讨论(0)
  • 2020-12-07 18:34

    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).

    0 讨论(0)
  • 2020-12-07 18:38

    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.

    0 讨论(0)
  • 2020-12-07 18:40

    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;
    
    0 讨论(0)
  • 2020-12-07 18:44

    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.

    0 讨论(0)
提交回复
热议问题