A way to send OpenCV Mat to MATLAB workspace without copying data?

When I write MEX files that use OpenCV functions, it is easy to transfer data from MATLAB to the MEX environment without copying the data. Is there a way to get data back to MATLAB in the same way? (That is, without copying data and causing MATLAB to crash ...)

Simple example:

#include "mex.h"
#include "/opencv2/core.hpp"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs,const mxArray *prhs[])
{

    Rows=mxGetM(prhs[0]);
    Cols=mxGetN(prhs[0]);
    Mat InMat(Cols,Rows,CV_64FC1,mxGetPr(prhs[0]));//Matlab passes data column-wise 
                                                   // no need to copy data - SUPER!

    InMat=InMat.t();//transpose so the matrix is identical to MATLAB one

    //Make some openCV operations on InMat to get OutMat...


    //Way of preventing the following code??

    plhs[0]=mxCreateDoubleMatrix(OutMat.rows,OutMat.cols,mxREAL);
    double *pOut=mxGetPr(plhs[0]);

    for (int i(0);i<OutMat.rows;i++)
      for (int j(0);j<OutMat.cols;j++)
         pOut[i+j*OutMat.rows]=OutMat.at<double>(i,j);

}

      

+3


source to share


1 answer


I usually do input and output in exactly the same way, adding a pointer to handle input and iterate over items in the output. But I think the output can be done in the same way as the input, although not without some copy. The only way to avoid copying is to create an output Mat

with a pointer from mxArray

and work with it in place. Of course, this is not always possible. But you can be graceful about how you copy the data.

You can use the same trick to attach a buffer to cv::Mat

which you use (me too!) To input data from MATLAB, but also to receive it. A twist on the data export trick is to use the copyTo

right so that it uses an existing buffer, the one from mxArray

in plhs[i]

.

Starting with an input like this:

double *img = mxGetPr(prhs[0]);
cv::Mat src = cv::Mat(ncols, nrows, CV_64FC1, img).t(); // nrows <-> ncols, transpose

      

You are doing some operation like resizing:

cv::Mat dst;
cv::resize(src, dst, cv::Size(0, 0), 0.5, 0.5, cv::INTER_CUBIC);

      

To get dst

in MATLAB: first carry over the output (in order to reorder the data in col-major order) , then create an output cv::Mat

with a pointer from plhs[0]

mxArray

and finally call copyTo

to fill the wrapper with the Mat

transposed data:

dst = dst.t(); // first!
cv::Mat outMatWrap(dst.rows, dst.cols, dst.type(), pOut); // dst.type() or CV_*
dst.copyTo(outMatWrap); // no realloc if dims and type match

      

It is very important that the size and data type are exactly the same for the next call copyTo

, so as not to reallocate outMatWrap

.

Note that when outMatWrap

destroyed, the buffer data

will not be freed because the reference count is 0 ( Mat::release()

not free .data

).




A possible template (by no means bulletproof!)

template <typename T>
void cvToMATLAB(cv::Mat mat, T *p)
{
    CV_Assert(mat.elemSize1() == sizeof(T));
    mat = mat.t();
    cv::Mat outMatWrap(mat.rows, mat.cols, mat.type(), p);
    mat.copyTo(outMatWrap);
}

      

This should be fine for channels> 1 if the MATLAB array size is also in pixel order (e.g. 3xMxN). Then use permute

as needed.


Note about copyTo

Conditions under which it copyTo

reallocates the destination buffer if the size or data type does not match:

opencv2 \ core \ mat.hpp line 347 (version 2.4.10) with my comments:

inline void Mat::create(int _rows, int _cols, int _type)
{
    _type &= TYPE_MASK;
    if( dims <= 2 && rows == _rows && cols == _cols && type() == _type && data )
        return;    // HIT THIS TO USE EXISTING BUFFER!
    int sz[] = {_rows, _cols};
    create(2, sz, _type); // realloc!
}

      

So, just make sure you set the correct size and data type, and the data will be in the buffer mxArray

instead of another. If you do this correctly, it copyTo

will use the buffer you specified, naming memcpy

for each line.

+3


source







All Articles