将一行cv::Mat转换为std::vector

10
我有一个非常简单的问题: 如何将 cv::Mat 中的一行数据取出并放入到 std::vector 中? cv::Mat 包含 doubles (为了说明问题,可以是任何简单的数据类型)。
阅读 OpenCV 文档很令人困惑,除非我收藏了页面,否则通过 Google 找不到两次文档页面,因为有太多内容且难以导航。
我已经找到了 cv::Mat::at(..) 来访问矩阵元素,但我记得在 C OpenCV 中至少有三种不同的访问元素的方式,它们都用于不同的目的... 记不清哪个用于哪个了 :/。
因此,尽管逐个复制矩阵元素肯定可行,但我正在寻找一种更高效、如果可能的话,比逐行 for 循环更加优雅的方法。

我认为这是一个重复的问题:http://stackoverflow.com/questions/5405070/cvmat-to-stdvector-conversion - piokuc
@piokuc 我看到了那个,但那是特定于平台(Visual Studio),针对 OpenCV 2.0(比今天的版本旧)并且涉及特定的 cv::Mat 包装器 cv::Mat_ - penelope
5个回答

33

这应该很简单:

m.row(row_idx).copyTo(v);

m是深度为CV_64Fcv::Matvstd::vector<double>


哇,这太棒了。但是,由于我实际上需要创建一个新的向量并填充数据,我认为在这种情况下只需将指针传递给向量构造函数会更好。 - penelope
这应该是答案。如此简单! - trozzel

17

OpenCV矩阵中的数据按行主序排列,因此每一行都是连续的。这意味着您可以将行中的数据解释为普通的C数组。以下示例直接来自文档

// compute sum of positive matrix elements
// (assuming that M is double-precision matrix)
double sum=0;
for(int i = 0; i < M.rows; i++)
{
    const double* Mi = M.ptr<double>(i);
    for(int j = 0; j < M.cols; j++)
        sum += std::max(Mi[j], 0.);
}
因此,最有效的方法是将裸指针传递给 std::vector
// Pointer to the i-th row
const double* p = mat.ptr<double>(i);

// Copy data to a vector.  Note that (p + mat.cols) points to the
// end of the row.
std::vector<double> vec(p, p + mat.cols);

使用返回的迭代器 begin()end() 比起这种方法会更慢,因为那些迭代器需要额外计算来支持行间距。


6

这里的文档中,你可以通过cv::Mat::row获取指定行,它将返回一个新的cv::Mat,你可以使用cv::Mat::begincv::Mat::end对其进行迭代。因此,以下内容应该可行:

cv::Mat m/*= initialize */;
// ... do whatever...
cv::Mat first_row(m.row(0));
std::vector<double> v(first_row.begin<double>(), first_row.end<double>());

请注意,我不了解任何OpenCV,但是通过搜索“OpenCV mat”,可以直接转到基本类型文档,根据该文档,这应该可以正常工作。
矩阵迭代器是随机访问迭代器,因此它们可以传递给任何STL算法,包括std::sort()。
这也是从文档中获取的,因此您实际上可以在不复制的情况下执行此操作。
cv::Mat m/*= initialize */;
// ... do whatever...
// first row          begin              end
std::vector<double> v(m.begin<double>(), m.begin<double>() + m.size().width);

要访问超过第一行的数据,我建议使用第一个片段,因为这样会更加简洁,而且由于数据类型似乎是引用计数的,所以不需要进行大量复制。


你的第二个例子中是不是想要用 m.size().width?请注意,OpenCV矩阵是以行优先方式存储的,而你正在添加行数,可能应该添加列数 - Ferdinand Beyer
@Ferdinand:哎呀,是的,那确实是我想要的。谢谢你的提示。 - Xeo
2
你可以直接使用 m.cols,而不是 m.size().width - Ferdinand Beyer

2
您可以使用cv::Rect
m(cv::Rect(0, 0, 1, m.cols)) 

将会给你第一行。

matrix(cv::Rect(x0, y0, len_x, len_y);

这意味着您将从matrix中获取一个sub_matrix,其左上角为(x0,y0),大小为(len_x, len_y)(row,col)


2
应该是 m(cv::Rect(0, 0, m.cols, 1)),而不是 1,m.cols - zhangxaochen

0

我认为这个可以运行,

一个例子:

Mat Input(480, 720, CV_64F, Scalar(100));

裁剪矩阵的第一行:

Rect roi(Point(0, 0), Size(720, 1));

然后:

std::vector<std::vector<double> > vector_of_rows;

vector_of_rows.push_back(Input(roi));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接