在OpenCV Mat中有效地查找元素

时间:2014-09-14 16:44:25

标签: c++ opencv search

假设我需要在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

的帖子

1 个答案:

答案 0 :(得分:4)

结合这两个答案并根据垫子类型(此处为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是double的类型,所以在我的机器上,double是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