http://stackoverflow.com/questions/27514658/way-to-send-opencv-mat-to-matlab-workspace-without-copying-the-data



When I write MEX files which use OpenCV functions it's easy to pass the data from MATLAB to the MEX environment without copying the data. Is there a way to return the data to MATLAB in the same manner? (That is, without copying the data and without causing MATLAB to crash...)

A 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's 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);

}



-----------------------------------------

Usually I do the input and output just like that, attaching a pointer to deal with the input and looping over elements on output. But, I think the output can be done in a similar manner to the input, although not without a copy of some sort. The only way to avoid a copy is to create the output Mat with a pointer from a mxArray and operate on it inplace. That's not always possible, of course. But you can be graceful about how you copy the data out.

You can exploit the same trick of attaching a buffer to a cv::Mat that you use (me too!) to bring data in from MATLAB, but also to get it out. The twist on the trick to exporting the data is to use copyTojust right so that it will use the existing buffer, the one from the 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 perform 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 into MATLAB: first transpose the output (for sake of reordering the data into col-major order) then create an output cv::Mat with the pointer from the plhs[0] mxArray, and finally call copyTo to fill out the wrapper Mat with the 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 to get the dimensions and data type exactly the same for the following call to copyTo to keep from reallocating outMatWrap.

Note that when outMatWrap is destroyed, the data buffer will not be deallocated because the reference count is 0 (Mat::release() does not deallocate .data).


Possible template (by no means bullet-proof!)

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 good for channels>1, as long as the size of the MATLAB array is in pixel order too (e.g. 3xMxN). Then use permute as needed.


Note about copyTo

The conditions under which copyTo will reallocate the destination buffer are if the dimensions or data type do 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 get the size and data type correct, and the data will end up in the mxArraybuffer instead of somewhere else. If you do it right, copyTo will use the buffer you specified, calling memcpy on each row.


Posted by uniqueone
,