在opencv中索引矩阵的最佳方法

Posted

技术标签:

【中文标题】在opencv中索引矩阵的最佳方法【英文标题】:Best way to indexing a matrix in opencv 【发布时间】:2015-11-17 06:52:12 【问题描述】:

假设AB 是大小相同的矩阵。 在Matlab 中,我可以使用如下简单的索引。

idx = A>0;
B(idx) = 0

如何在OpenCV 中执行此操作?我应该使用

for (i=0; ... rows)
    for(j=0; ... cols)
        if (A.at<double>(i,j)>0) B.at<double>(i,j) = 0;

像这样?有没有更好(更快、更高效)的方法?

另外,在OpenCV,当我尝试

Mat idx = A>0;

变量idx 似乎是一个CV_8U 矩阵(不是布尔值,而是整数)。

【问题讨论】:

“最佳”是什么意思?最快的?最容易编码?最好保养?许多函数使用“掩码”,它是 8 位输入图像,用于判断像素是“活动”(非零)还是“非活动”(零)。从灰度图像中,您可以使用 cv::Mat mask = A &gt; 0 轻松创建蒙版。对于您的示例,您可以使用 B.setTo(cv::Scalar(0), mask) 谢谢,米卡。我的意思是“更好”,“更快,更高效”。我编辑了。因为我是一个 Matlab 人,我只是在犹豫是否使用嵌套循环,并考虑使用一些为更快实现而设计的内置函数的另一种方法。 我建议你在 Python 中这样做:B[A>0] = 0 B 有多少频道?典型尺寸是多少? 【参考方案1】:

您可以轻松转换此 MATLAB 代码:

idx = A > 0;
B(idx) = 0;

// same as 

B(A>0) = 0;

到 OpenCV 为:

Mat1d A(...)
Mat1d B(...)

Mat1b idx = A > 0;
B.setTo(0, idx) = 0;

// or

B.setTo(0, A > 0);

关于性能,在 C++ 中,处理原始指针通常更快(取决于启用的优化)(但可读性):

for (int r = 0; r < B.rows; ++r)

    double* pA = A.ptr<double>(r);
    double* pB = B.ptr<double>(r);
    for (int c = 0; c < B.cols; ++c)
    
        if (pA[c] > 0.0) pB[c] = 0.0;
    

另请注意,在 OpenCV 中没有任何 布尔 矩阵,但它是一个 CV_8UC1 矩阵(又名unsigned char 的单通道矩阵),其中0 表示false ,并且任何值 &gt;0 都是 true(通常是 255)。

评估

请注意,这可能会有所不同,具体取决于 OpenCV 启用的优化。您可以在您的 PC 上测试下面的代码以获得准确的结果。

以毫秒为单位的时间:

          my results           my results      @AdrienDescamps
          (OpenCV 3.0 No IPP)  (OpenCV 2.4.9)

Matlab  : 13.473     
C++ Mask: 640.824              5.81815         ~5
C++ Loop: 5.24414              4.95127         ~4

注意:我不完全确定 OpenCV 3.0 的性能下降,所以我只是说:在您的 PC 上测试下面的代码以获得准确的结果。

正如@AdrienDescamps 在 cmets 中所述:

OpenCV 3.0 的性能下降似乎与 OpenCL 选项有关,该选项现在在比较运算符中启用。

C++ 代码

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()

    // Random initialize A with values in [-100, 100]
    Mat1d A(1000, 1000);
    randu(A, Scalar(-100), Scalar(100));

    // B initialized with some constant (5) value
    Mat1d B(A.rows, A.cols, 5.0); 

    // Operation: B(A>0) = 0;

    
        // Using mask

        double tic = double(getTickCount());
        B.setTo(0, A > 0);
        double toc = (double(getTickCount()) - tic) * 1000 / getTickFrequency();

        cout << "Mask: " << toc << endl;
    
    
        // Using for loop

        double tic = double(getTickCount());
        for (int r = 0; r < B.rows; ++r)
        
            double* pA = A.ptr<double>(r);
            double* pB = B.ptr<double>(r);
            for (int c = 0; c < B.cols; ++c)
            
                if (pA[c] > 0.0) pB[c] = 0.0;
            
        
        double toc = (double(getTickCount()) - tic) * 1000 / getTickFrequency();

        cout << "Loop: " << toc << endl;
    


    getchar();
    return 0;

Matlab 代码

% Random initialize A with values in [-100, 100]
A = (rand(1000) * 200) - 100;
% B initialized with some constant (5) value
B = ones(1000) * 5; 

tic
B(A>0) = 0; 
toc

更新

OpenCV 3.0 在函数setTo 中使用IPP 优化。如果您启用了该功能(您可以通过cv::getBuildInformation() 进行检查),您将获得更快的计算速度。

【讨论】:

你确定c++掩码的时间吗?我在发布模式下编译 opencv(2.4) 得到 5 毫秒,在调试中得到 9 毫秒,c++ 循环时间为 4 毫秒。 @AdrienDescamps 这是我得到的结果,但我没有启用太多优化。正如我所提到的,结果可能会有所不同(现在加粗)。我将您的结果编辑为答案。感谢您的反馈! @AdrienDescamps 实际上使用 OpenCV 2.4.9 我得到的结果和你的一样。我在没有启用 IPP 的情况下使用 OpenCV 3.0 进行测试。我不确定这种性能下降,但这只是 “您可以在您的 PC 上测试下面的代码以获得准确的结果。”的另一个论据。感谢您指出这一点! OpenCV 3.0 的性能下降似乎与 OpenCL 选项有关,该选项现在在比较运算符中启用。 @AdrienDescamps 奇怪的是这会导致性能下降......非常感谢您指出这一点!我已将您的评论添加到答案中。【参考方案2】:

Miki 的回答非常好,但我只是想对性能问题进行一些澄清以避免任何混淆。

确实,使用 OpenCV 实现图像过滤器(或任何算法)的最佳方法是使用原始指针,如 Miki(C++ 循环)的第二个 C++ 示例所示。 使用 at 函数也是正确的,但速度要慢很多。

但是,大多数时候,您不必担心这一点,您可以简单地使用 OpenCV 的高级功能(Miki 的第一个示例,C++ Mask)。它们经过了很好的优化,通常几乎与指针上的低级循环一样快,甚至更快。

当然,也有例外(我们刚刚发现了一个),您应该始终针对您的具体问题进行测试。

现在,关于这个具体问题:

此处的示例中,高级函数比低级循环慢得多(慢 100 倍)不是正常情况,因为 OpenCV 的其他版本/配置的时间要低得多。 问题似乎是,当使用 OpenCL 编译 OpenCV3.0 时,第一次调用使用 OpenCL 的函数会产生巨大的开销。如果您使用 OpenCV3.0,最简单的解决方案是在编译时禁用 OpenCL(如果您有兴趣,请参阅 here 了解其他可能的解决方案)。

【讨论】:

非常有帮助的讨论

以上是关于在opencv中索引矩阵的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

两个 Java OpenCV 矩阵之间的距离

C++ OpenCV 中的快速索引

OpenCV如何获取GLCM灰度共生矩阵

在 OpenCV 中确定矩阵的类型

如何通过另一个具有位置(索引)的垫子访问opencv中的矩阵数据

python使用openCV把原始彩色图像转化为灰度图使用矩阵索引的方式对数据数据进行剪裁(image cropping)