Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

From the doc If the array header is built on top of user-allocated data, you should handle the data by yourself ... in your case the returned cv::Mat.data points to the memory of matrix variable that is local and is destroyed on function exit. Consequently the cv::Mat.data becomes a dangling pointer

Some examples:

CASE (1) This works because OpenCV is the owner of the img memory. The local img is destroyed but its memory is safely managed by OpenCV because is used by main()

cv::Mat transformToCVMatrix()
{
  //img is local and has its own memory
  cv::Mat img(rows,cols,CV_64FC1);
  ...
  return img; 
}
void main()
{
    cv::Mat mat = transformToCVMatrix();
}

CASE (2) this works because the user-allocated data is within the scope of the caller function main(). The memory is still valid after transformToCVMatrix returns

const int rows = 2;
const int cols = 3;
double userdata[rows][cols] = {
    { 0, 1, 2 },
    { 3, 4, 5 }
};

cv::Mat transformToCVMatrix()
{
    //here img points to an user-allocated data that is GLOBAL
    cv::Mat img(rows,cols,CV_64FC1,userdata);
  ...
  return img;
}
void main()
{
    cv::Mat mat = transformToCVMatrix();
}

CASE (3) this (is your case) do not works because OpenCV can't manage the user-allocated data img.data' points to userdata that is destroyed on return... than img.data becomes a dangling pointer

cv::Mat transformToCVMatrix()
{
    const int rows = 2;
    const int cols = 3;
    double userdata[rows][cols] = {
        { 0, 1, 2 },
        { 3, 4, 5 }
    };
   //we are making a header for user-allocated data is LOCAL. this is not safe
    cv::Mat img(rows,cols,CV_64FC1,userdata);
  ...
  return img;
}

In your case the solution is to copy the user data into the cv::Mat to as suggested by @berak using ::clone()

return cv::Mat(rows, cols, CV_64FC1, matrix).clone();

or build a Matx with the flag copyData=true

return Mat(cv::Matx<double, rows, cols>(*matrix), true);

both options will switch your case to CASE (1)

From the doc If the array header is built on top of user-allocated data, you should handle the data by yourself ... in your case the returned cv::Mat.data points to the memory of matrix variable that is local and is destroyed on function exit. Consequently the cv::Mat.data becomes a dangling pointerpointer.

see also this about method to declare an image processing function

Some examples:

CASE (1) This works because OpenCV is the owner of the img memory. The local img is destroyed but its memory is safely managed by OpenCV because is used by main()

cv::Mat transformToCVMatrix()
{
  //img is local and has its own memory
  cv::Mat img(rows,cols,CV_64FC1);
  ...
  return img; 
}
void main()
{
    cv::Mat mat = transformToCVMatrix();
}

CASE (2) this works because the user-allocated data is within the scope of the caller function main(). The memory is still valid after transformToCVMatrix returns

const int rows = 2;
const int cols = 3;
double userdata[rows][cols] = {
    { 0, 1, 2 },
    { 3, 4, 5 }
};

cv::Mat transformToCVMatrix()
{
    //here img points to an user-allocated data that is GLOBAL
    cv::Mat img(rows,cols,CV_64FC1,userdata);
  ...
  return img;
}
void main()
{
    cv::Mat mat = transformToCVMatrix();
}

CASE (3) this (is your case) do not works because OpenCV can't manage the user-allocated data img.data' points to userdata that is destroyed on return... than img.data becomes a dangling pointer

cv::Mat transformToCVMatrix()
{
    const int rows = 2;
    const int cols = 3;
    double userdata[rows][cols] = {
        { 0, 1, 2 },
        { 3, 4, 5 }
    };
   //we are making a header for user-allocated data is LOCAL. this is not safe
    cv::Mat img(rows,cols,CV_64FC1,userdata);
  ...
  return img;
}

In your case the solution is to copy the user data into the cv::Mat to as suggested by @berak using ::clone()

return cv::Mat(rows, cols, CV_64FC1, matrix).clone();

or build a Matx with the flag copyData=true

return Mat(cv::Matx<double, rows, cols>(*matrix), true);

both options will switch your case to CASE (1)

From the doc If the array header is built on top of user-allocated data, you should handle the data by yourself ... in your case the returned cv::Mat.data points to the memory of matrix variable that is local and is destroyed on function exit. Consequently the cv::Mat.data becomes a dangling pointer.

see also this about method to declare an image processing function

Some examples:

CASE (1) This works because OpenCV is the owner of the img memory. The local img is destroyed but its memory is safely managed by OpenCV because is used by main()

cv::Mat transformToCVMatrix()
{
  //img is local and has its own memory
  cv::Mat img(rows,cols,CV_64FC1);
  ...
  return img; 
}
void main()
{
    cv::Mat mat = transformToCVMatrix();
}

CASE (2) this works because the user-allocated data is within the scope of the caller function main(). The memory is still valid after transformToCVMatrix returns

const int rows = 2;
const int cols = 3;
double userdata[rows][cols] = {
    { 0, 1, 2 },
    { 3, 4, 5 }
};

cv::Mat transformToCVMatrix()
{
    //here img points to an user-allocated data that is GLOBAL
    cv::Mat img(rows,cols,CV_64FC1,userdata);
  ...
  return img;
}
void main()
{
    cv::Mat mat = transformToCVMatrix();
}

CASE (3) This works with care until you don't explicitly delete userdata. You have to delete[] userdata somewhere. Is common case that userdata is allocated by some other 3rd function or API. Maybe it will dispose the memory for you. In this case you have to be sure that doesn't happen while you are using the Mat otherwise it will become a dangling Mat. But if you manage the memory you can delete it using he Mat.data pointer.

cv::Mat transformToCVMatrix()
{
  int rows = 2, cols = 3;
  double *userdata = new double[cols*rows];
  // fill with test values;
  for (int r = 0; r < rows; r++)
    for (int c = 0; c < cols; c++)
    {
      int idx = c + r*cols;
      userdata[idx] = double(idx);
    }
  //we are making a header for user-allocated on the heap.
  //This will survive after function returns
  cv::Mat img(rows, cols, CV_64FC1, userdata);
  return img;
}
void main()
{
    cv::Mat mat = transformToCVMatrix();
    // use your mat but remember to delete the memory
    delete[](double *)mat.data;
}

CASE (4) this (is your case) do does not works work because OpenCV can't manage the user-allocated data data. img.data' points to userdata that is destroyed on return... than img.data becomes a dangling pointer

cv::Mat transformToCVMatrix()
{
   const int rows = 2;
   const int cols = 3;
   double userdata[rows][cols] = {
         { 0, 1, 2 },
         { 3, 4, 5 }
   };
   //we are making a header for user-allocated data is LOCAL. this is not safe
   cv::Mat img(rows,cols,CV_64FC1,userdata);
  ...
  return img;
}

In your case the solution is to copy the user data into the cv::Mat to as suggested by @berak using ::clone()

return cv::Mat(rows, cols, CV_64FC1, matrix).clone();

or build a Matx with the flag copyData=true

return Mat(cv::Matx<double, rows, cols>(*matrix), true);

both options will switch your case to CASE (1)