user3392074
user3392074

Reputation: 31

OpenCV fast mat element and neighbour access

I use OpenCV (C++) Mat for my matrix and want to acces single Mat elements as fast as possible. From OpenCV tutorial, I found code for efficient acces:

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

For my problem, I need to access a Mat element and its neighbours (i-1,j-1) for a calculation. How can I adapt the given code to acces a single mat element AND its surrounding elements? Since speed matters, I want to avoid Mat.at<>(). What is the most efficient way to acces a Mat value and its neighbour values?

Upvotes: 1

Views: 4467

Answers (3)

DanyAlejandro
DanyAlejandro

Reputation: 1468

For any future readers: Instead of reading the answers here, please read this blog post https://www.learnopencv.com/parallel-pixel-access-in-opencv-using-foreach/ for a benchmark-based analysis of this functionality, as some of the answers are a bit off the bat.

From that post you can see that the fastest way to access pixels is using the forEach C++ Mat function. If you want the neighborhood it depends of the size; if you're looking for the usual squared 3x3 neighborhood, use pointers like this:

Mat img = Mat(100,100,CV_8U, Scalar(124)); // sample mat
uchar *up, *row, *down; // Pointers to rows
uchar n[9]; // neighborhood

for (int y = 1 ; y < (img.rows - 1) ; y++) {
    up = img.ptr(y - 1);
    row = img.ptr(y);
    down = img.ptr(y + 1);
    for (int x = 1 ; x < (img.cols - 1) ; x++) {
        // Examples of how to access any pixel in the 8-connected neighborhood
        n[0] = up[x - 1];
        n[1] = up[x];
        n[2] = up[x + 1];
        n[3] = row[x - 1];
        n[4] = row[x];
        n[5] = row[x + 1];
        n[6] = down[x - 1];
        n[7] = down[x];
        n[8] = down[x + 1];
    }
}

This code can still be optimized but the idea of using row pointers is what I was trying to convey; this is just a bit faster than using the .at() function and you might have to do benchmarking to notice the difference (in versions of OpenCV 3+). You might want to use .at() before deciding to optimize pixel access.

Upvotes: 0

marol
marol

Reputation: 4074

You can directly refers to Mat::data:

template<class T, int N>
T GetPixel(const cv::Mat &img, int x, int y) {
    int k = (y * img.cols + x) * N;
    T pixel;
    for(int i=0;i<N;i++)
        pixel[i] = *(img.data + k + i);
    return pixel;
}

template<class T,int N>
void SetPixel(const cv::Mat &img, int x, int y, T t) {
    int k = (y * img.cols + x) * N;
    for(int i=0;i<N;i++)
        *(img.data + k + i) = t[i];
}

template<>
unsigned char GetPixel<unsigned char, 1>(const cv::Mat &img, int x, int y) {
    return *(img.data + y * img.cols + x);
}

template<>
void SetPixel<unsigned char, 1>(const cv::Mat &img, int x, int y, unsigned char p) {
    *(img.data + y * img.cols + x) = p;
}




int main() {
    unsigned char r,g,b;
    int channels = 3;
    Mat img = Mat::zeros(256,256, CV_8UC3);
    for(int x=0;x<img.cols;x+=2)
        for(int y=0;y<img.rows;y+=2) 
             SetPixel<cv::Vec3b, 3>(img, x, y, cv::Vec3b(255,255,255));


    Mat imgGray = Mat::zeros(256,256, CV_8UC1);
    for(int x=0;x<imgGray.cols;x+=4)
        for(int y=0;y<imgGray.rows;y+=4) 
             SetPixel<unsigned char, 1>(imgGray, x, y, (unsigned char)255);



    imwrite("out.jpg", img);
    imwrite("outGray.jpg", imgGray);

    return 0;
}

That is pretty fast I think.

out.jpg:

out

outGray.jpg:

outGray

Upvotes: 0

herohuyongtao
herohuyongtao

Reputation: 50667

The pixel and its neighbor pixels can be formed a cv::Rect, then you can simply use:

cv::Mat mat = ...;
cv::Rect roi= ...; // define it properly based on the neighbors defination
cv::Mat sub_mat = mat(roi);

In case your neighbors definition is not regular, i.e. they cannot form a rectangle area, use mask instead. Check out here for examples.

Upvotes: 1

Related Questions