shashashamti2008
shashashamti2008

Reputation: 2337

Passing mxArray to mexCallMatlab fails

I am writing a mex file in which conv2 function is called. This mex file will get an image of size (M, N) and apply convolution several time using conv2.

#include "mex.h"

void myconv( mxArray *Ain, mxArray *Kernel, mxArray *&Aout )
{

    mxArray *rhs[3];

    rhs[0] = mxCreateNumericMatrix( 0, 0, mxDOUBLE_CLASS, mxREAL );
    rhs[1] = mxCreateNumericMatrix( 0, 0, mxDOUBLE_CLASS, mxREAL );
    rhs[2] = mxCreateString       ( "same" );

    double *ainPtr = mxGetPr( Ain   );
    mxSetPr( rhs[0], ainPtr         );
    mxSetM ( rhs[0], mxGetM(Ain)    );
    mxSetN ( rhs[0], mxGetM(Ain)    );

    double *kernelPtr = mxGetPr( Kernel );
    mxSetPr( rhs[1], kernelPtr          );
    mxSetM ( rhs[1], mxGetM(Kernel)     );
    mxSetN ( rhs[1], mxGetN(Kernel)     );

    mexCallMATLAB(1, &Aout, 3, rhs, "conv2");

    mxSetPr( rhs[0], NULL ); 
    mxSetPr( rhs[1], NULL );

}

void myconv_combine( mxArray *Ain, mxArray *&Aout )
{

    mxArray *mask = mxCreateDoubleMatrix( 1, 5, mxREAL );

    double *maskPtr = mxGetPr( mask );
    maskPtr[0] = 0.05; 
    maskPtr[1] = 0.25; 
    maskPtr[2] = 0.4; 
    maskPtr[3] = 0.25; 
    maskPtr[4] = 0.05;

    mxArray *maskTranspose = mxCreateDoubleMatrix( 0, 0, mxREAL );
    mxSetPr( maskTranspose, maskPtr      );
    mxSetM ( maskTranspose, mxGetN(mask) );
    mxSetN ( maskTranspose, mxGetM(mask) );

    mxArray *AinConvolved = mxCreateDoubleMatrix( (mwSize)mxGetM(Ain), (mwSize)mxGetN(Ain), mxREAL );
    double *AinConvolvedPtr = mxGetPr( AinConvolved );
    myconv( Ain, mask, AinConvolved );

    // Some modifications.
    mxArray *Temp = mxCreateDoubleMatrix( (mwSize)mxGetM(Ain), (mwSize)mxGetN(Ain), mxREAL );
    double *TempPtr = mxGetPr( Temp );
    for( int i = 0; i < (mwSize)mxGetM(Ain)*(mwSize)mxGetN(Ain); i++ )
            TempPtr[ i ] = 2.0*AinConvolvedPtr[ i ];

    // Some other convolution.
    mxArray *TempConvolved = mxCreateDoubleMatrix( (mwSize)mxGetM(Ain), (mwSize)mxGetN(Ain), mxREAL );
    double *TempConvolvedPtr = mxGetPr( TempConvolved );
    myconv( Temp, maskTranspose, TempConvolved );

    // Some other modifications.
    double *AoutPtr = mxGetPr( Aout );
    for( int i = 0; i < (mwSize)mxGetM(Ain)*(mwSize)mxGetN(Ain); i++ )
            AoutPtr[ i ] = 2.0*TempConvolvedPtr[ i ];


}


void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{

   mxArray *Ain  = mxCreateDoubleMatrix( 100, 100, mxREAL );
   mxArray *Aout = mxCreateDoubleMatrix( 100, 100, mxREAL );

   myconv_combine( Ain, Aout );


}

In my actual code, when it reaches the line:

myconv( Temp, maskTranspose, TempConvolved );

MATLAB crashes which I have no clue why this happens and unfortunately I could not duplicate the same error in the example code I provided above. In my actual code, the image is convolved successfully by line:

myconv( Ain, mask, AinConvolved );

However, as soon as it wants to apply a second convolution:

myconv( Temp, maskTranspose, TempConvolved );

It crashes and when I debug it, it occurs when mexCallMATLAB is called on myconv function. What can be the difference between Temp/TempConvolved and Ain/AinConvolved that makes the former crash at the time of mexCallMATLAB?

Could someone kindly help me fix this issue?

Upvotes: 1

Views: 250

Answers (1)

chappjc
chappjc

Reputation: 30589

Reusing data buffers

Reusing the data pointer from mxArray *mask for mxArray *maskTransposed is asking for trouble, as MATLAB has rigorous mechanisms for reference counting as shared-data arrays are an important part of MATLAB's memory optimizations. Instead, duplicate the whole thing with mxDuplicateArray:

mxArray *maskTranspose = mxDuplicateArray(mask);

There is an undocumented mxCreateSharedDataCopy that emulates MATLAB's lazy-copy mechanism, but that's really overkill for a 5-element array.

Superfluous-to-problematic mxArray initialization prior to mexCallMATLAB

Also, do not bother initializing mxArray *AinConvolved before calling mexCallMATLAB. Just pass a NULL pointer and it will create it for you. If you don't it will just wipe the old one (send it to garbage collection) and create a fresh one for the output of conv2. Which reminds me this demonstrates how this is a problem in your code:

mxArray *AinConvolved = mxCreateDoubleMatrix(mxGetM(Ain), mxGetN(Ain), mxREAL);
double *AinConvolvedPtr0 = mxGetPr(AinConvolved);
myconv(Ain, mask, AinConvolved);
double *AinConvolvedPtr = mxGetPr(AinConvolved);
mexPrintf("%p\n%p\n", AinConvolvedPtr0, AinConvolvedPtr);

Output in MATLAB:

00000000B852FA20
0000000026B8EB00

As you can see, if you try to use the pointer you got with mxGetPr before using mexCallMATLAB, you're probably using the wrong data, possibly already deallocated memory..

Automatic separable filtering with imfilter

Also, note that if you have imfilter, you don't need to implement separable convolution because it has that functionality built in. Just have a look at imfilter.m and note the isSeparable function. See here for more information.

Try that, I'll post a test.

Upvotes: 1

Related Questions