【问题标题】:OpenCV fast mat element and neighbour accessOpenCV 快速垫元素​​和邻居访问
【发布时间】:2014-06-10 02:13:48
【问题描述】:

我将 OpenCV (C++) Mat 用于我的矩阵,并希望尽快访问单个 Mat 元素。从 OpenCV 教程中,我找到了高效访问的代码:

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

对于我的问题,我需要访问 Mat 元素及其邻居 (i-1,j-1) 进行计算。如何调整给定的代码以访问单个 mat 元素及其周围元素?由于速度很重要,我想避免Mat.at&lt;&gt;()。 访问 Mat 值及其相邻值的最有效方法是什么?

【问题讨论】:

  • 不要太害怕 mat.at() 虽然它在调试模式下进行缓慢的边界检查,但它实际上在发布时相当快。停止任何过早的优化。

标签: c++ opencv image-processing matrix


【解决方案1】:

对于任何未来的读者:请阅读此博客文章 https://www.learnopencv.com/parallel-pixel-access-in-opencv-using-foreach/,而不是阅读此处的答案,以对该功能进行基于基准的分析,因为其中一些答案有点离题。

从那篇文章中,您可以看到访问像素的最快方法是使用 forEach C++ Mat 函数。如果你想要邻居,这取决于大小;如果您正在寻找通常的平方 3x3 邻域,请使用如下指针:

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];
    }
}

这段代码仍然可以优化,但使用行指针的想法是我试图传达的;这只是比使用 .at() 函数快一点,您可能需要进行基准测试才能注意到差异(在 OpenCV 3+ 版本中)。在决定优化像素访问之前,您可能需要使用 .at()。

【讨论】:

    【解决方案2】:

    你可以直接引用 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;
    }
    

    我认为这相当快。

    输出.jpg:

    outGray.jpg:

    【讨论】:

      【解决方案3】:

      像素和它的相邻像素可以组成一个cv::Rect,那么你可以简单地使用:

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

      如果您的邻居定义不规则,即它们不能形成矩形区域,请改用遮罩。查看here 获取示例。

      【讨论】:

      • 这是迭代整个 Mat 最有效的方法吗?
      • @user3392074 我不确定这是否是最有效的方法。但它肯定比cv::Mat.at() 快​​。
      • 有什么理由应该更快?
      • 因为 opencv 处理 cv::Rect 的方式很可能是直接在循环中访问 Mat::data 。所以你有一个函数调用和 N 个像素访问。在 Mat::at 的情况下,您有 N 个调用,每个调用都有一个 ppixel 访问。此外,在调试模式下,它会检查无效的边界。
      猜你喜欢
      • 2012-03-28
      • 1970-01-01
      • 1970-01-01
      • 2021-11-28
      • 2014-02-05
      • 2016-10-01
      • 2018-03-26
      • 1970-01-01
      • 2015-09-14
      相关资源
      最近更新 更多