OpenCV:vector<vector<Point>> 到 vector<Mat> 的转换,引用调用的问题

Posted

技术标签:

【中文标题】OpenCV:vector<vector<Point>> 到 vector<Mat> 的转换,引用调用的问题【英文标题】:OpenCV: Conversion of vector<vector<Point>> to vector<Mat>, problems with call by reference 【发布时间】:2014-11-03 10:53:04 【问题描述】:

我试图使用 OpenCV 2.4.9 从图像中提取轮廓线。findContours 函数完成大部分工作,但它返回轮廓线 作为类型vector &lt; vector &lt; Point &gt; &gt;。 我需要将它们转换为vector &lt; Mat &gt; 以供以后使用。 我使用Mat 类的构造函数来完成这项工作,一切正常,直到我通过引用调用将结果从一个函数传递给另一个函数。 以下代码重现了错误:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

void getCont(Mat& img, vector<Mat>& cLines, int thresh)

    //binarize the image
    Mat imgBin;
    threshold(img, imgBin, thresh, 255, THRESH_BINARY_INV);

    //find contour lines
    vector<vector<Point>> contours;
    findContours(imgBin, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    //convert vector<vector<Point>> to vector<Mat>
    for (int i = 0; i < contours.size(); i++)
        cLines.push_back(Mat(contours[i])); 

    cerr << "cLines[0] in getCont:\n";
    cerr << "cLines[0].rows: " << cLines[0].rows << "\n";
    cerr << "cLines[0].cols: " << cLines[0].cols << "\n";
    cerr << "cLines[0].channels(): " << cLines[0].channels() << "\n";
    cerr << "cLines[0].type(): " << cLines[0].type() << "\n";
    cerr << "cLines[0].row(0): " << cLines[0].row(0) << "\n";
    cerr << endl << endl;


int main()

    Mat img = imread("leaf.jpg", 0);
    int thresh = 124;

    vector<Mat> cLines;
    getCont(img, cLines, thresh);

    cerr << "cLines[0] in main:\n";
    cerr << "cLines[0].rows: " << cLines[0].rows << "\n";
    cerr << "cLines[0].cols: " << cLines[0].cols << "\n";
    cerr << "cLines[0].channels(): " << cLines[0].channels() << "\n";
    cerr << "cLines[0].type(): " << cLines[0].type() << "\n";
    cerr << "cLines[0].row(0): " << cLines[0].row(0) << "\n";

    return 0;

当我尝试打印cLines 的第一个元素的第一行时,错误发生在main 中,在return 语句之前的行中。对于不同的输入图像,我要么收到一条消息,告诉我 .exe 无法正常工作,必须退出,要么实际打印出值,但它们与 getCont 函数的输出不同(在 main ,我得到负值,所以看起来有一些溢出)。我在 Windows 8 64 位机器上使用 Visual Studio 2013 Express(但我使用的是 OpenCV 的 x86 DLL 库)。任何人都可以在不同的系统上重现该错误吗?

我以为有一些隐式类型转换,所以在getContmain中打印出cLines的大小和类型,但结果是一样的。当我将getCont函数的代码放入main时,错误不会发生,这样我就避免了额外的函数调用。 此外,当我更换循环时,一切正常

for (int i = 0; i < contours.size(); i++)
    cLines.push_back(Mat(contours[i])); 

通过以下方式:

for (int i = 0; i < contours.size(); i++)

    vector<Point> currPts = contours.at(i);
    Mat currLine(currPts.size(), 1, CV_32SC2);
    for (int j = 0; j < currPts.size(); j++)
    
        currLine.at<Vec2i>(j, 0).val[0] = currPts.at(j).x;
        currLine.at<Vec2i>(j, 0).val[1] = currPts.at(j).y;
    
    cLines.push_back(currLine);

有人知道怎么回事吗?

【问题讨论】:

cLines的声明不应该和Mat currLine(currPts.size(), 1, CV_32SC2);类似吗?在您的主要内容中,没有任何地方说CV_32SC2 @a-Jays:感谢您的回答。我在声明cLines时没有显式设置类型,但是在main和getCont中,cLines[0].type()的输出都是12(即枚举中的CV_32SC2)。 对我来说效果很好。您是否成功加载图像?你可以尝试imshow它来验证。 是的,我检查了图像是否加载成功。听到它在您的系统上工作很有趣。当 cLines 从 getCont 传递到 main 时,会发生奇怪的事情。 @herohuyongtao 我认为你很幸运。问题是Mat 构造函数默认不复制数据,当getCont 返回时vector 超出范围。我看到负数,但即使缓冲区已被释放,我也可以看到正确的数据可能仍在内存中。您的knowledge points 有什么新东西吗? ;) 【参考方案1】:

您使用了正确的构造函数,但错误地接受了第二个参数的默认值。 Mat 构造函数的声明以 std::vector 作为输入:

//! builds matrix from std::vector with or without copying the data
template<typename _Tp> explicit Mat(const vector<_Tp>& vec, bool copyData=false);

cv::Mat 构造函数的online documentation 声明:

copyData – 用于指定 STL 向量或旧式 CvMat 或 IplImage 的基础数据是否应复制到 (true) 或与 (false) 新构造的矩阵共享的标志。复制数据时,分配的缓冲区使用 Mat 引用计数机制进行管理。共享数据时,引用计数器为 NULL,在矩阵未被破坏之前,您不应释放数据。

你需要做的:

cLines.push_back(Mat(contours[i],<b>true</b>));

否则当您返回main 时,向量将超出范围,并且getCont 中声明的vector&lt;vector&lt;Point&gt;&gt; contours 的数据缓冲区将被释放。

对于 cv::VecPoint_Point3_copyData 的默认值为 true,与 std::vector 不同。

【讨论】:

以上是关于OpenCV:vector<vector<Point>> 到 vector<Mat> 的转换,引用调用的问题的主要内容,如果未能解决你的问题,请参考以下文章

Opencv 的各个vector容器探究

在 C++ 中将 vector<Rect> OpenCV 转换为 vector<vector<float>>

如何像矩阵元素一样访问opencv中的vector<vector<Point>>轮廓?

将 Vector<float> 中的数据复制到 Vector<Mat> (Opencv/C++)

opencv::meanStdDev 带有矢量<vector<double>>

Vector<Vec3b> 与 OpenCV 中 Vector<int> 的区别