1

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?

afp_2008
  • 1,940
  • 1
  • 19
  • 46

1 Answers1

1

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.

Community
  • 1
  • 1
chappjc
  • 30,359
  • 6
  • 75
  • 132
  • I changed my code and used `mxArray *AinConvolved= NULL;` and `mxArray *TempConvolved = NULL;`. I also tried `myconv( Temp, mask, TempConvolved );` to eliminate the potential of causing reference counting and used `mask` instead of `maskTranspose`. However, it still does not work and the problem happens in the same place as before, where `TempConvolved` cannot be computed. – afp_2008 Nov 22 '14 at 06:01
  • 1
    @A2009 How about placement of `mxGetPr(AinConvolved)` and `mxGetPr( TempConvolved )` after their calls to `mexCallMATLAB`? Is there any code that can reproduce that you can post, perhaps on gist.github.com for a large amount of code? – chappjc Nov 22 '14 at 06:21
  • Let me see how I can reproduce the erroneous part to better clarify the issue. I appreciate your help. – afp_2008 Nov 22 '14 at 06:52
  • I have uploaded a code here: https://gist.github.com/afalahat/cb48ede7eab0d92d161d – afp_2008 Nov 24 '14 at 01:42
  • @A2009 Your code has the same issue of resuing data buffers. The buffer set by `mxSetPr` must be created only by `mxMalloc` or `mxCalloc`. Don't create `Bt1[0]` with the `double*` from `mxGetPr( prhs[0] )`. Just pass `prhs[0]` to `Reduce`. – chappjc Nov 24 '14 at 22:42
  • The reason I prefer to do this is that the input image is of size `(NY, NX, NT)`, where `NT` is the number of the images in time. I actually want to do this task for every image at every timestep. – afp_2008 Nov 24 '14 at 22:47
  • 1
    @A2009 Understood, but it's the [same issue as before](http://stackoverflow.com/a/27068004/2778484), specifically [this discussion point](http://chat.stackoverflow.com/transcript/message/20077783#20077783). If you pass in a pyramid, two options: put each level as it's own input argument or use a cell array with [`mxGetCell`](http://www.mathworks.com/help/matlab/apiref/mxgetcell.html?refresh=true). It's very easy to pass a cell. – chappjc Nov 24 '14 at 22:51
  • Got you. Does using `mxGetCell` force me to copy the values? – afp_2008 Nov 24 '14 at 23:55