Kuchenschaf
Kuchenschaf

Reputation: 23

OpenCV: Conversion of vector<vector<Point>> to vector<Mat>, problems with call by reference

I was trying to extract contour lines from an image, using OpenCV 2.4.9.
The findContours function does most of the work, but it returns the contour lines
as type vector < vector < Point > >. I need to convert them to type vector < Mat > for later uses.
I used a constructor of the Mat class to do the job and everything works fine until I pass the result from one function to the other via call by reference. The following code reproduces the error:

#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;
}

The error occurs in main, in the line preceding the return statement, when I try to print out the first row of the first element of cLines. For different input images, I either get a message, which tells me, that the .exe is not working properly and has to quit or the values are actually printed out, but they differ from the output of the getCont function (in main, I get negative values, so it looks like there is some overflow). I'm using Visual Studio 2013 Express on Windows 8, 64 bit machine (but I'm using the x86 DLL libraries of OpenCV). Can anyone reproduce the error on a different system?

I thought there was some implicit type cast, so I printed out the size and type of cLines in getCont and main, but the results are the same. The error does not occur, when I put the code of the getCont function into main, so that I avoid the additional function call. Also, everything works fine, when I replace the loop

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

by the following:

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);
}

Does anyone know what's going on?

Upvotes: 2

Views: 2921

Answers (1)

chappjc
chappjc

Reputation: 30589

You are using the right constructor but incorrectly accepting the default for the second parameter. The declaration for the Mat constructor taking std::vector as an input:

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

The online documentation for the cv::Mat constructors states:

copyData – Flag to specify whether the underlying data of the STL vector or the old-style CvMat or IplImage should be copied to (true) or shared with (false) the newly constructed matrix. When the data is copied, the allocated buffer is managed using Mat reference counting mechanism. While the data is shared, the reference counter is NULL, and you should not deallocate the data until the matrix is not destructed.

You need to do:

cLines.push_back(Mat(contours[i],true));

Otherwise the vector will go out of scope when you return to main and the data buffer for the vector<vector<Point>> contours declared in getCont will have been deallocated.

For cv::Vec, Point_, and Point3_, the default for copyData is true, unlike for std::vector.

Upvotes: 2

Related Questions