高效地在 OpenCV Mat 中查找元素

Posted

技术标签:

【中文标题】高效地在 OpenCV Mat 中查找元素【英文标题】:Find element in OpenCV Mat efficiently 【发布时间】:2014-09-14 16:44:25 【问题描述】:

假设我需要在 cv::Mat 中找到一个特定元素,在我的情况下它可以是一个行向量(尽管对于更一般的情况,Mat 可以不止一维)。 目标的数据类型可以很简单,char、int、double等。

现有帖子:How to find if an item is present in a std::vector? 解释了如何在 std::vector 中查找元素。因此,一种方法可以是:1)将 cv::Mat 转换为 std::vector; 2)使用帖子中的方法找到元素。 但是,我需要每行执行数百次此搜索操作。当我有数百行需要处理时,性能可能会成为问题。

我想知道上述方法(转换+搜索)的性能如何,有没有更有效的方法来做到这一点(也许直接使用 cv::Mat 搜索元素而不进行转换)?

p.s:这是Converting a row of cv::Mat to std::vector的帖子

【问题讨论】:

“查找元素”到底是什么意思?你需要一个二进制答案(比如它是否包含'17'?)?索引列表? x,y 位置 ?出现次数('其中 i==17)? 我想要垫子中目标的 x,y 位置(在我的情况下只有一维位置)。 @marol 给出了答案的二进制版本,可以对其进行修改以轻松获取索引。 【参考方案1】:

结合这两个答案并根据您得到的垫子类型(此处为 CV_64F):

bool findValue(const cv::Mat &mat, double value) 
    for(int i = 0;i < mat.rows;i++) 
        const double* row = mat.ptr<double>(i);
        if(std::find(row, row + mat.cols, value) != row + mat.cols)
            return true;
    
    return false;

(请参阅find docs 了解更多信息)。当然,首先将 mat 行转换为向量,然后在该向量上使用 std::find 比直接在指向行数组的指针上使用 find 慢。

编辑:经过更多研究,开发通用版本并不难:

template <class T>
bool findValue(const cv::Mat &mat, T value) 
    for(int i = 0;i < mat.rows;i++) 
        const T* row = mat.ptr<T>(i);
        if(std::find(row, row + mat.cols, value) != row + mat.cols)
            return true;
    
    return false;

我在更复杂的数据类型上对其进行了测试:

cv::Mat matDouble = cv::Mat::zeros(10, 10, CV_64F);
cv::Mat matRGB = cv::Mat(10, 10, CV_8UC3, cv::Scalar(255, 255, 255));

std::cout << findValue(matDouble, 0.0) << std::endl;
std::cout << findValue(matDouble,1.0) << std::endl;
std::cout << findValue(matRGB, cv::Scalar(255, 255, 255)) << std::endl;
std::cout << findValue(matRGB, cv::Scalar(255, 255, 254)) << std::endl;

令我惊讶的输出是:

1
0
0 // should be 1, right?
0

问题在于 cv::Scalar 大小结构。无论我们使用的是什么版本的构造函数(即一个、两个、三个或四个参数),大小都是……常量。这并不奇怪,因为这仍然是相同的结构,在我的机器上大小是 32 字节(默认情况下 cv::Scalar 是双精度类型,所以在我的机器上双精度是 8 字节和 4 * 8 = 32)。所以查找出错了,因为它假定数组中元素的大小为 32 个字节,它应该是 3 个字节。

所以不要将 std::find 与 cv::Scalar 一起使用! 但是它可以很好地和高效地处理原始数据类型。

EDIT2(在 berak 的评论之后):

是的,您可以将 cv::Vec3b 与 find 一起使用,它似乎运行良好,尽管它没有做更多的测试,只是简单地正确测试:

cv::Mat matRGB = cv::Mat(10, 10, CV_8UC3, cv::Scalar(255, 255, 255));

std::cout << findValue(matRGB, cv::Vec3b(255, 255, 255)) << std::endl;
std::cout << findValue(matRGB, cv::Vec3b(255, 255, 254)) << std::endl;

(您仍然必须在 Mat 构造函数中使用 Scalar,但这并不重要,并且 Mat 已正确初始化)。现在输出如预期:

1
0

【讨论】:

你试过了吗:findValue(matRGB, Vec3b(255, 255, 255))?标量是 ptr 模板 的错误数据类型

以上是关于高效地在 OpenCV Mat 中查找元素的主要内容,如果未能解决你的问题,请参考以下文章

了解一个 Mat 元素 Opencv 的值

Opencv中Mat高效率 左右上下平移一定的像素

OpenCV中高效的像素遍历方法,写出工程级像素遍历代码

【OPENCV】cv::Mat像素遍历方法比较

OpenCV基于字节指针进行高效像素遍历

opencv怎么给mat赋值