Frage

I want to pass such a cell matrix created in Matlab as input argument to mex function,

for i=1:5,
    p{i}=rand(3,4);
end

and then return it as as an 3 dimensional double array as output argument. intended syntax:

Parray = convert(p);

where Parray is a 3-by-4-by-5 numerical array and P(:,:,i) = p{i};

The following code I am using can be successfully built into desired mex function:

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <matrix.h>
#include <cstring>
#include <string>
#include "mex.h"


using namespace std;

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

if (nlhs>=1 && nrhs>=1){
        int       nsubs =2,  index;
        mwIndex   subs[]={0, 0};
        mxArray   *tmp;
        double     **buf;

        int cameraNum = mxGetNumberOfElements(prhs[0]);
        mwIndex  dims[]={cameraNum,3,4};
        plhs[0] = mxCreateNumericArray(3,dims,mxDOUBLE_CLASS,mxREAL);
        mexPrintf("there are %d cameras in the input cell.",cameraNum);

        /* allocate memory  for storing pointers */

        buf = (double**)mxGetData(plhs[0]);

        for (int i=0; i<cameraNum; i++){
            subs8[1] = i;

            // get the cell (i,1)
            index = mxCalcSingleSubscript(prhs[1], nsubs, subs);
            tmp = mxGetCell(prhs[0],index);

            buf[i] = (double*)mxGetData(tmp); 
            int rownum = mxGetM(tmp);
            int colnum = mxGetN(tmp);
            mexPrintf("\n No. %d camera matrix is: \n",i);
            for(int m=0;m<rownum;m++){
                for(int n=0; n<colnum;n++){
                    mexPrintf("%lf\t",buf[i][m*colnum+n]);
                }
            }

        }
        mxFree(buf);
    }

    return;
}

However, it causes Matlab's collapse due to unknown reason. What is the reason? and How can I get rid of the problem?

Additionally, if the desired syntax is :

Parray = convert(p);

and Parray is exactly the same as p, how to implement in C++ mex function?

Thanks

War es hilfreich?

Lösung 2

I finally realized it by an example with comments. I here post the example implementation code here for any interested others' reference.

/*==========================================================
* testmex01.cpp - example in MATLAB External Interfaces
*
* Illustrates how to use some C++ language features in a MEX-file.
*
* This is a MEX-file for MATLAB.
* no Copyright; 2013 The LC Factorization, Inc.
*
*========================================================*/
/* $Revision: 0.0 $ */

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "mex.h"
#include <matrix.h>
//
#include <Eigen/Dense>
#include "Eigen/Eigen"
#include "Eigen/LU"
#include "Eigen/SVD"
//
using namespace Eigen;
using namespace std;
#include "mpir.h"
#include "mpreal.h"

#include <cstring>
#include <string>

using namespace mpfr;
//using namespace std;

//extern void _main();
//
///****************************/
//class MyData {
//
//public:
//  void display();
//  void set_data(double v1, double v2);
//  MyData(double v1 = 0, double v2 = 0);
//  ~MyData() { }
//private:
//  double val1, val2;
//};
//
//MyData::MyData(double v1, double v2)
//{
//  val1 = v1;
//  val2 = v2;
//}
//
//void MyData::display()
//{
//#ifdef _WIN32
//  mexPrintf("Value1 = %g\n", val1);
//  mexPrintf("Value2 = %g\n\n", val2);
//#else
//  cout << "Value1 = " << val1 << "\n";
//  cout << "Value2 = " << val2 << "\n\n";
//#endif
//}
//
//void MyData::set_data(double v1, double v2) { val1 = v1; val2 = v2; }
//
///*********************/
//
//static
////void
//double mexcpp(
//      double num1,
//      double num2
//      )
//{
//#ifdef _WIN32
//  mexPrintf("\nThe initialized data in object:\n");
//#else
//  cout << "\nThe initialized data in object:\n";
//#endif
//  MyData *d = new MyData; // Create a  MyData object
//  d->display();           // It should be initialized to
//                          // zeros
//  d->set_data(num1,num2); // Set data members to incoming
//                          // values
//#ifdef _WIN32
//  mexPrintf("After setting the object's data to your input:\n");
//#else
//  cout << "After setting the object's data to your input:\n";
//#endif
//  d->display();           // Make sure the set_data() worked
//  delete(d);
//  flush(cout);
//  return num1+num2;
//}

////////////// how to handle structure
struct mystruct
{
char   *a;
double *b;
double *c;
double *d;
};

void printme( char *a, double *b, double *c, double *d )
{
    mexPrintf(a);
    mexPrintf( "\n a = %s,\tb = %f,\tc = %f,\td = %f\n", a, b[0], c[0], d[0] );
    mexPrintf( "a = %s,\tb = %f,\tc = %f,\td = %f\n", a, b[99], c[99], d[99] );
}
/////////////  how to handle structure


void mexFunction(
    int          nlhs,
    mxArray      *plhs[],
    int          nrhs,
    const mxArray *prhs[]
)
{
    //double      *vin1, *vin2;

    /* Check for proper number of arguments */

    //if (nrhs != 2) {
    //  mexErrMsgIdAndTxt("MATLAB:mexcpp:nargin", 
    //          "MEXCPP requires two input arguments.");
    //} else if (nlhs >= 1) {
    //  mexErrMsgIdAndTxt("MATLAB:mexcpp:nargout",
    //          "MEXCPP requires no output argument.");
    //}

    //vin1 =  mxGetPr(prhs[0]);//(double *) mxGetPr(prhs[0]);
    //vin2 = mxGetPr(prhs[1]); //(double *) mxGetPr(prhs[1]);


    ////////////// Here are the multiple precision settings

    const int digits = 256;
    // Setup default precision for all subsequent computations
    // MPFR accepts precision in bits - so we do the conversion 
    mpreal::set_default_prec(mpfr::digits2bits(digits));
    const mpreal pi          =    mpfr::const_pi();

    const int n = 5;

    typedef Matrix<mpreal,Dynamic,Dynamic>  MatrixXmp;
    typedef Matrix<mpreal,Dynamic,1>        VectorXmp;

    MatrixXmp A = MatrixXmp::Random(n,n);
    VectorXmp b = VectorXmp::Random(n);
    VectorXmp x =  A.lu().solve(b);
    VectorXmp residue0 = A *x -b;//.norm();
    mpreal residue = residue0.norm();


    if(nlhs>=1){

        const char *fieldnames[] = {"a", "b" , "c" , "d"};
        int T = 100;
        struct mystruct X;
        plhs[0] = mxCreateStructMatrix(1 , 1 , 4 , fieldnames);

        string str2= pi.toString();// first struct field is char Pi
        mxSetFieldByNumber(plhs[0] ,0 , 0 , mxCreateString(str2.c_str()));

        for(int i = 1 ; i < 4 ; i++){
            mxArray *data = mxCreateNumericMatrix(T, 1 , mxDOUBLE_CLASS,mxREAL);

            for(int j = 0; j < T; j++ ) {
                *(mxGetPr(data) + j) = (i * T) + j;
            }
            mxSetFieldByNumber(plhs[0] ,0 , i , data);
        }

        int strlenn= mxGetN(mxGetFieldByNumber( plhs[0], 0, 0 ))+1;
        //char tempchar[digits+2]; // Here the digits can be any positive integer
        X.a = (char*)mxCalloc(strlenn,sizeof(char));//tempchar;
        // Allocate enough memory to hold the converted string
        printf("\n X.a before being allocated a string is %s ; size of X.a is %d \n",X.a,sizeof(X.a));
        //_strdup(pi.toString().c_str()); //mxCalloc(n,sizeof(char)) cannot be used here..
        mxGetString(mxGetFieldByNumber( plhs[0], 0, 0 ),X.a ,strlenn);//strlen(X.a) //sizeof(X.a)// here the 3rd input argument can be any integer?
        printf("The strlen of X.a is %d, size of X.a is %d, mxGetN+1 is %d , sizeof char is %d \n",strlen(X.a),sizeof(X.a),strlenn,sizeof(char));
        //mxGetString( mxGetFieldByNumber( plhs[0], 0, 0 ), X.a, sizeof(X.a));//strlen(X.a)*sizeof(mxChar)+1);//strlen()
        X.b = mxGetPr( mxGetFieldByNumber( plhs[0], 0, 1 ) );
        X.c = mxGetPr( mxGetFieldByNumber( plhs[0], 0, 2 ) );
        X.d = mxGetPr( mxGetFieldByNumber( plhs[0], 0, 3 ) );

         printme(X.a, X.b, X.c, X.d);
         //mxFree(X);
    }


    //second output argument is matrix: A, which can be double or char*
    // double type output
    if (nlhs>=2&&nrhs<1){
        double *two2three;
        plhs[1]= mxCreateDoubleMatrix(n,n, mxREAL);//mxCreateCellMatrix(n,n);
        two2three= mxGetPr(plhs[1]);//mxGetCell(plhs[1],);
        for (int i=0;i<n;i++)
            for(int j=0; j<n; j++){
                two2three[i+j*n]=A(i, j).toDouble();
            }
    }

    if (nlhs>=2 && nrhs>=1){ // try to output A as cell matrix

        mwSize ndims[] ={5,5} ;
        int nsubs=2;
        mwIndex subs[2];
        plhs[1]= mxCreateCellArray(2, ndims);//create a 2D array with 5x5 dimensions
        //mxSetName(plhs[1], "amoeba");

        for (int i=0;i<n;i++)
            for(int j=0; j<n; j++){
                subs[0]=i;subs[1]=j;
                int index = mxCalcSingleSubscript(plhs[1], nsubs, subs); // nsubs 
                mxSetCell(plhs[1],index,mxCreateString(_strdup(A(i, j).toString().c_str())));//two2three[i+j*n]=A(i, j).toDouble();
            }
    }

    //The 3rd argument is b vecoter, with double and char* options,
    if (nlhs>=3 && nrhs<1){ 
        double  *three2two;
        plhs[2]= mxCreateDoubleMatrix(n,1, mxREAL);//mxCreateCellMatrix(n,n);
        three2two = mxGetPr(plhs[2]);//mxGetCell(plhs[1],);
        for (int i=0;i<n;i++)
            three2two[i]=b(i).toDouble();
    }

    if (nlhs>=3&&nrhs>=1){//try to set output b as cell vector
        plhs[2]=mxCreateCellMatrix(n, 1);
        for (int i = 0; i < n; i++) {
            mxSetCell(plhs[2],i,mxCreateString(_strdup(b(i).toString().c_str())));
        }
    }

    //with double and char* options for testing double/char matrix argument type
    if (nlhs>=4&& nrhs<1){// x as double
        double  *solution;
        plhs[3]= mxCreateDoubleMatrix(n,1, mxREAL);//mxCreateCellMatrix(n,n);
        solution = mxGetPr(plhs[3]);//mxGetCell(plhs[1],);
        for (int i=0;i<n;i++)
            solution[i]= x(i).toDouble();
    }

    // the 4th cell/string char* matrix argument
    if (nlhs>=4 && nrhs>=1){//try to set output x as cell vector
        //memory allocation
        plhs[3]=mxCreateCellMatrix(n, 1);
        for (int i = 0; i < n; i++) {//format for output
            mxSetCell(plhs[3],i,mxCreateString(_strdup(x(i).toString().c_str())));
        }
    }

    // Euclidean norm of residue vector in mpreal, by char* output
    if (nlhs>=5)
        plhs[4] = mxCreateString(residue.toString().c_str());

    //the 6th residue vector; in Char* matrix/vector , as mpreal multiple precision
    if (nlhs>=6){
        //always allocate memory first
        plhs[5]=mxCreateCellMatrix(n, 1);

        //int m = digits + 60;       
        ///* Stuff the input into a string buffer. */
        //std::string strbuf;

        //char **line;
        ///* Create line buffers for the individual vector elements. */
        //for (int i=0; i<n;i++){
        //  line[i] =(char *) mxCalloc(m,sizeof(char));  //char * writable = new char[str.size() + 1];
        //  strbuf = residue0(i).toString();;
        //  std::copy(strbuf.begin(), strbuf.end(), line[i]);
        //  line[i][strbuf.size()] = '\0';
        //  mxSetCell(plhs[5],i,mxCreateString(line[i]));
        //  mxFree(line[i]);
        //}

        for (int i = 0; i < n; i++) {
            //note the conversion method here:
            mxSetCell(plhs[5],i,mxCreateString(_strdup(residue0(i).toString().c_str())));
            //Other methods of converting string into const char* or char*
            // residue0(i).toString().data();// const char*
        }
        //plhs[5] = mxCreateString(residue0(n-1).toString().c_str());//array;
    }

    //test data type conversion from mpreal to double, excellent result;
    if (nlhs>=7&&nrhs>=1){
        double *xconversion = (double *) mxGetPr(prhs[0]);// (double *)mxGetData(prhs[0]);

        mpreal yconversion =xconversion[0];

        mexPrintf("\n The input argument in double is %lf \n                mpreal double is %lf string is %s; ",xconversion[0],yconversion.toDouble(),yconversion.toString().c_str());
        plhs[6] = mxCreateString(yconversion.toString().c_str());
    }

    //第八个输出参数是3维的数字矩阵;把包含相机的Cell向量中的相机取出,放到数字型三维矩阵并输出
    if (nlhs>=8&&nrhs>=2){
        if (!mxIsCell(prhs[1])){
            //mexErrMsgTxt(" 2nd input argument must be cell matrix.");
            mexPrintf("\n 2nd input argument must be cell matrix.\n");
        }

        mwIndex   subs8[] ={0}; // first 
        mxArray   *strtmp;//*cell_element_ptr;

        int cameraNum = mxGetNumberOfElements(prhs[1]);
        // multidimensional array output "3 x 4 x nViews"
        mwIndex  dims[]={3,4,cameraNum};//for output double{ 3, 4, cameraNum} array

        plhs[7] = mxCreateNumericArray(3,dims,mxDOUBLE_CLASS,mxREAL);

        //初始化mpreal类型的矩阵和临时变量
        MatrixXmp eigenM(3,4);
        mpreal tempdbl;

        //逐个把第2个输入参数的double矩阵里的元素,赋值给输出参数的数字型矩阵
        for (int i=0;i<cameraNum;i++){
            *subs8 = i;
            //把右侧Cell输入向量的第i个矩阵元素取出到mxArray* 临时变量;
            strtmp = mxGetCell(prhs[1],mxCalcSingleSubscript(prhs[1], 1, subs8));
            int nrows = mxGetM(strtmp); //实际已经知道是 3x4
            int ncols = mxGetN(strtmp);

            //逐个矩阵(页面i)显示矩阵
            mexPrintf("\n No. %d camera matrix is: \n",i+1);

            for (int m=0; m<3;m++){
                for(int n=0;n<4;n++){
                    tempdbl = ((double*)mxGetPr(strtmp))[m+n*nrows];
                    //逐个元素取出的方式是指针操作; 序数的计算方法值得注意
                    ((double*)mxGetPr(plhs[7]))[i*nrows*ncols+m+n*nrows]= tempdbl.toDouble();

                    if(i==cameraNum-1)
                        eigenM(m,n) = tempdbl;

                    mexPrintf("%lf\t",((double*)mxGetPr(strtmp))[m+n*nrows]);//Note the difference between them
                }
                mexPrintf("\n");
            }
        }
        // 显示mpreal格式的最后一个3x4矩阵的逐列
        mexPrintf("\n");
        for (int i=0;i<4;i++){
            mexPrintf("\n Column No. %d :\n",i+1);
            for (int j=0; j<3;j++){
                mexPrintf(" %s \n",eigenM(j,i).toString().c_str());
            }
        }
        //最末一个双精度元素转化为字符串,然后字符,再显示(精度不能设置)mpreal更好
        std::ostringstream strss;
        strss  << ((double*)mxGetPr(strtmp))[2+3*3];
        std::string strnum = strss.str();

        mpreal mprrealnum=((double*)mxGetPr(strtmp))[2+3*3];

        char *charnum =new char[strnum.size()+1];
        charnum[strnum.size()]=0;
        memcpy(charnum,strnum.c_str(),strnum.size());

        mexPrintf("\n The converted double string is %s with size %d \n",charnum,strnum.size());
        mexPrintf(" The mpreal based conversion is %s \n",mprrealnum.toString().c_str());


        //if (mxIsCell(strtmp)){
        //  mexPrintf("\n strtmp is cell \n");
        //}
        //else{
        //  mexPrintf("\n strtmp is NOT cell \n");
        //}

        //if (mxIsDouble(strtmp)){
        //  mexPrintf("\n strtmp is double \n");
        //}
        //else{
        //  mexPrintf("\n strtmp is NOT double \n");
        //}

        //mexPrintf("The mxGetM of prhs[1] is %d, mxGetN is %d \n",mxGetM(prhs[1]),mxGetN(prhs[1]));
        //mexPrintf("\n There are %d cameras in the input cell.\n",cameraNum);

        //buf = mxArrayToString(strtmp);
        //mexPrintf("The converted string is %s\n",buf);


        //  mexPrintf("\n No. %d camera matrix is: \n",i);
        //  for(int m=0;m<rownum;m++){
        //      for(int n=0; n<colnum;n++){
        //          mexPrintf("%lf\t",buf8[i][m*colnum+n]);
        //      }
        //  }

        //}
        //mxFree(buf8);
    }

    return;
}

Andere Tipps

In plhs[0] you have created a 3D mxArray, which has one buffer of doubles. So in buf8 = (double**)mxGetData(plhs[0]), mxGetData returns a void* to that buffer and you cannot simply cast it to double** and expect to get an array of valid pointers. As an aside, I suggest using static_cast in place of C-style casting.

I used this to create a matrix field in a struct: one option is to create a temp variable, and then to assign its value to a field of the struct:

// Create temp variable
mxArray* array = convertVectorToMxArray(mat, nb_rows, nb_cols);  
const std::string temp_name = array_name + "_temp";
int ret = engPutVariable(ep, temp_name.c_str(), array);

// Set variable to struct field
const std::string cmd = std::string(array_name + " = " + temp_name + "; ");
matlabExecute(ep, cmd);

// Delete array
mxDestroyArray(array);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top