为啥将 cv::Mat 的一列复制到向量中失败?

Posted

技术标签:

【中文标题】为啥将 cv::Mat 的一列复制到向量中失败?【英文标题】:Why does copying a column of a cv::Mat into a vector fail?为什么将 cv::Mat 的一列复制到向量中失败? 【发布时间】:2013-10-25 18:37:41 【问题描述】:

考虑以下代码示例。为什么下面标记的行会在运行时触发断点/异常?

int main() 
    Mat m1 = Mat::zeros(10, 1, CV_32FC1);
    Mat m2 = Mat::zeros(10, 3, CV_32FC1);

    vector<float> v1(m1); // works
    Mat m2sub = m2.col(0);
    Mat m2subClone = m2.col(0).clone();
    vector<float> v2(m2subClone); // works
    vector<float> v3(m2sub); // doesn't work
return 0;

这似乎很奇怪,因为被调用的是在 mat.hpp 中:

template<typename _Tp> inline Mat::operator std::vector<_Tp>() const 
    std::vector<_Tp> v;
    copyTo(v);
    return v; // <- breaks here

并且 copyTo 似乎在 memcpy 数据。

它没有给出错误消息,但我在堆栈跟踪中看到它在 return 语句处中断,然后深入到“operator new”和“ntdll.dll!RtlpAllocateHeap()”的某个地方。

奇怪的是,在我的完整代码中,它在一个稍微不同的地方中断:在 memcpy 的 copyTo(v) 内,并抛出“访问冲突写入位置 0x0000000001F43D4C。”。我的完整代码看起来和上面的一模一样,但是矩阵更大。

编辑:如果在上面的示例中,我将矩阵更改为

    Mat m1 = Mat::zeros(5900, 1, CV_32FC1);
    Mat m2 = Mat::zeros(5900, 3, CV_32FC1);

sn-p 在与我的完整代码相同的地方失败,出现访问冲突错误。

我有超过 2GB 的可用 RAM,并且该应用程序被编译为 64 位应用程序,因此它不应该是“内存不足”问题 (?)

【问题讨论】:

根据您使用的平台,行为似乎存在一些差异(请参阅 Luiz 回答下的 cmets)。您使用的是什么编译器和 OpenCV 版本? @Aurelius 我正在使用 VS2012,64 位,OpenCV 来自 Git,几个月前(3 月至 7 月的东西。Git 显示 2.4.4/2.4.5,但是 dll 的后缀为 249 。我也许可以深入挖掘并找出是否相关。) 【参考方案1】:

我不太了解 OpenCV Mat 类,但我猜矩阵中的列有一些共享的东西,所以记忆它可能不是一个好主意。检查 Mat::row 方法的 OpenCV 文档(here,Mat::col 方法具有相同的关于“共享标头”的参数),有一个注释表明以下 不是一个好主意

Mat A;
...
A.row(i) = A.row(j); // will not work

并且您应该使用以下内容:

A.row(j).copyTo(A.row(i));

所以,也许在你的代码中你应该使用这个:

vector<float> v3;
m2sub.copyTo(v3);

【讨论】:

这并没有解决 OP 的问题。如问题所述,copyTo() 的调用方式与您在转换中建议的方式完全相同。 不过,我只是在这里测试了代码(MSVC 10、Windows 7、32 位二进制),它并没有与 m2sub.copyTo(v3); (即使它与 vector v3(m2sub); 中断)。 我的意思是,问题可能与双重删除或类似问题有关,因为 OpenCV 实现了引用计数(而标准向量没有)。在这个页面有一些关于使用克隆(在 m2subClone 中使用的那个)和 copyTo 的讨论:docs.opencv.org/doc/tutorials/core/… 这实际上是一个有趣的观察。这两个版本都使用 Xcode 和 OpenCV 2.4.5 对我造成破坏。奇怪的是,只有 10 个元素的 OP 代码对我来说并没有破坏。 代码在 memcpy 函数处中断。我在 OpenCV 文档中的某处(无法再次找到它)读到内部指针是以基于行的方式处理的。所以,由于 col 方法只是返回一个引用,内存复制把事情搞砸了。我做了一个快速测试并更改为 Mat m2 = Mat::zeros(5900, 300, CV_32FC1);此外,我将每个对 col 的调用更改为对 row 的调用。代码工作正常。所以,对我来说,问题是由于子部分没有被克隆并且列指针不是连续的(更多信息在这里itanveer.com/2012/05/18/linear-algebra-in-opencv-post2)

以上是关于为啥将 cv::Mat 的一列复制到向量中失败?的主要内容,如果未能解决你的问题,请参考以下文章

需要一种更快的方法将 cv::Mat 转换为一维向量形式

Uchar 到 cv::Mat 的向量

向量的 OpenCV 向量到 cv::Mat

试图将 cv::Mat 数据复制到 uchar *

如何将单个图像从图像矢量复制到临时 cv::Mat?

std::vector<cv::Vec3b> 到 cv::Mat