Access to image values using a Mat of coordinates

2019-08-13 18:49发布

问题:

In openCV(C++) I have an image IMG and I'd like to extract the values of each pixel at positions store in coor. Is it possible to get this pixel values in an efficient way? Is a foor loop (using .at(i,j)) efficient ?Is there a built-in function for doing this? This is my code:

cv::Mat IMG = cv::imread("image.png",CV_LOAD_IMAGE_GRAYSCALE);
cv::Mat coor = (cv::Mat_<float>(3,2) << 1, 0, 0, 1, 2, 2); // search coordinates
cv::Mat pixel_values;
// and I'd like to do something like this:
//This should result in a matrix with the same size as *coor*
pixel_values = IMG.at(coor); // similar to the matrix accesing method in Matlab. 

回答1:

Threr are different ways to access the value of a "pixel" in a cv::Mat. Have you read the documentation of OpenCV?

I suggest you to start form here

Edit I've corrected the code: this code works with msvc 2015 and opencv (3.1) but also > 2 is ok

#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
#include <cstdint>
using namespace std;

#if defined(NDEBUG)
#pragma comment(lib, "opencv_world310.lib")
#else
#pragma comment(lib, "opencv_world310d.lib")
#endif // 


cv::Mat GetPixelsFromMat( const cv::Mat& I, const std::vector<cv::Point2f>& points )
{
    // some pre-condition:
    cv::Mat res( 1, (int)points.size( ), CV_8UC1 );

    int i = 0;
    for( const auto& point : points )
        res.ptr( 0 )[ i++ ] = I.at<uchar>( cvRound( point.y ), cvRound( point.x ) );

    return res;
}

int main( int argc, char* argv[] )
{
    cv::Mat testImg( 1, 10, CV_8UC1 );
    for( int i = 0; i < 10; ++i )
        testImg.ptr( 0 )[ i ] = i;

    std::vector<cv::Point2f> points;
    points.push_back( cv::Point2f( 1, 0 ) );
    points.push_back( cv::Point2f( 5, 0 ) );
    points.push_back( cv::Point2f( 9, 0 ) );

    cv::Mat pixelsMap = GetPixelsFromMat( testImg, points );

    if( pixelsMap.ptr( 0 )[ 0 ] == 1 )
        cout << "OK 0" << endl;
    else
        cout << "FAIL 0" << endl;

    if( pixelsMap.ptr( 0 )[ 1 ] == 5 )
        cout << "OK 1" << endl;
    else
        cout << "FAIL 1" << endl;

    if( pixelsMap.ptr( 0 )[ 2 ] == 9 )
        cout << "OK 2" << endl;
    else
        cout << "FAIL 2";


    return EXIT_SUCCESS;
}

This is a big semplification but I think it's a starting point.



回答2:

If you already know the type of your matrix, you can easily go with @elvis.dukaj answer. You just need to iterate over your coordinates and retrieve corresponding pixel value.

But, if you need something more general that supports:

  • an input matrix of any type and channels
  • an input list of coordinates being either a cv::Mat or a std::vector, with coordinates of any type (int, float, etc...)

you may find this getPixels function useful:

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

Mat getPixels(InputArray _src, InputArray _coords)
{
    // Get src matrix
    Mat src = _src.getMat();

    // Get coords matrix, eventually convert to integer type
    Mat coords = _coords.getMat();
    if (coords.type() != CV_32S)
    {
        coords.convertTo(coords, CV_32S);
    }
    // Check coordinates
    int n = coords.checkVector(2, CV_32S);
    CV_Assert(n > 0);

    // Create output matrix
    Mat dst(n, 1, src.type());

    // Get pixel values at given coordinates
    int esz = src.elemSize();
    Point* coords_ptr = (Point*)coords.data;
    uchar* dst_ptr = dst.data;

    for (int i = 0; i < n; i++, dst_ptr += esz)
    {
        memcpy(dst_ptr, src.ptr(coords_ptr[i].y) + coords_ptr[i].x * esz, esz);
    }

    return dst;
}


int main() 
{

    Mat img(5, 5, CV_8UC1);
    randu(img, Scalar(0, 0, 0), Scalar(10, 10, 10));

    vector<Point> coor{ Point(1, 0), Point(0, 1), Point(2, 2) };
    //cv::Mat coor = (cv::Mat_<float>(3, 2) << 1, 0, 0, 1, 2, 2);

    Mat out = getPixels(img, coor);

    cout << out;

    return 0;
}