Reading .mat file using C: how to read cell-struct

2020-08-11 04:41发布

问题:

I am basically attempting to translate Matlab code in C code. This is the extension of my previous question.

In Matlab, I have used cell-structures which holds matrices (double) of variable sizes. Here is a toy example of what my *.mat file is supposed to store:

Matlab Code:

A = [[1 2 3]; [5 7 1]; [3 5 9]];
B = [[2 4];[5 7]];
Creator = 'DKumar';

nFilters = 2;

Filters{1} = [[-1.0 -1.0 -1.0]; [-1.0 8 -1.0]; [-1.0 -1.0 -1.0]];
Filters{2} = 2.0*[[-1.0 -1.0]; [-1.0 8]; [-1.0 -1.0]];

cd('/home/dkumar/CPP_ExampleCodes_DKU/Read_mat_File');
save('Test_FILE.mat', 'A', 'B', 'Creator', 'nFilters', 'Filters');

C-code: The function "matread_Matrix" reads matrices stored in *.mat properly. It's the function "matread_Cell", which is supposed to read cell-structure, is not working.

#include <stdio.h>
#include <stdlib.h>
#include "/usr/local/MATLAB/R2011b/extern/include/mat.h"

mxArray *arr;
mxArray *C_CELL;
/* declare  a 2 x 1 array of pointers to access the cell array in C */
mxArray *cellArray[2];

struct stDoubleMat{
   double* pValueInField;
   int nRows, nCols;
};

void matread_Matrix(const char *file, const char *FieldName2Read, struct stDoubleMat* poDoubleMat_LOC)
{
    printf("Reading file %s...\n\n", file);

    //Open file to get directory
    MATFile* pmat = matOpen(file, "r");

    if (pmat == NULL) {
      printf("Error opening file %s\n", file);
      return;
    }

    // extract the specified variable
    arr = matGetVariable(pmat, FieldName2Read);

    double *pr;
    if (arr != NULL && !mxIsEmpty(arr)) {
        // copy data
        mwSize num = mxGetNumberOfElements(arr);

        pr = mxGetPr(arr);

        if (pr != NULL) {
        poDoubleMat_LOC->pValueInField = pr;
            poDoubleMat_LOC->nRows  = mxGetM(arr);
            poDoubleMat_LOC->nCols  = mxGetN(arr);
        }
    printf("matread_Matrix \n") ;
        printf( "oDoubleMat_LOC.nRows %i ; oDoubleMat_LOC.nCols %i \n", poDoubleMat_LOC->nRows , poDoubleMat_LOC->nCols);

    }else{
        printf("nothing to read \n") ;
    }


    // close the file
    matClose(pmat);

    return;
}

void matread_Cell(const char *file, const char *FieldName2Read, int CellIndex)
{
    printf("Reading file %s...\n\n", file);

    //Open file to get directory
    MATFile* pmat = matOpen(file, "r");

    if (pmat == NULL) {
      printf("Error opening file %s\n", file);
      return;
    }

    // extract the specified variable
    C_CELL = matGetVariable(pmat, FieldName2Read);
    cellArray[CellIndex] = mxGetCell(C_CELL, CellIndex);

    double* p2 = (double*)cellArray[CellIndex];
    int nRows  = mxGetM(cellArray[CellIndex]);
    int nCols  = mxGetN(cellArray[CellIndex]);

    printf(" From inside matread_Cell : nRows %i and nCols %i \n", nRows, nCols);

    int i2;
    for (i2 = 0; i2 < nRows*nCols; i2++)
    {
    printf(" copied value : %f \n", *p2);
    p2 = p2 +1;
    }

    // close the file
    matClose(pmat);
}


int main(int argc, char **argv)
{
    const char *FileName = "/home/dkumar/CPP_ExampleCodes_DKU/Read_mat_File/Test_FILE.mat";
    const char *FieldName2Read = "A";

    struct stDoubleMat oDoubleMat; 
    matread_Matrix(FileName, FieldName2Read, &oDoubleMat);
    double* v = oDoubleMat.pValueInField;


    printf("From main \n");
    printf( "oDoubleMat.nRows %i ; oDoubleMat.nCols %i \n", oDoubleMat.nRows , oDoubleMat.nCols);

    int i;
    for (i = 0; i < oDoubleMat.nCols*oDoubleMat.nRows; i++)
    {
        printf(" copied value : %f \n", *v);
        v = v +1;
    }

    // Reading the structure
    const char *FieldName2Read2 = "Filters";
    matread_Cell(FileName, FieldName2Read2, 0);
    matread_Cell(FileName, FieldName2Read2, 1);


    // cleanup the mex-array
    mxDestroyArray(arr);
    mxDestroyArray(C_CELL);
    /* How to delete mxArray of pointer : should this be a array of pointers */
    //mxDestroyArray(cellArray[0]);
    //mxDestroyArray(cellArray[1]);

    return 0;
}

Output:

$ gcc -g -o Test Read_MatFile_DKU_2.c -I/usr/local/MATLAB/R2011b/extern/include -L/usr/local/MATLAB/R2011b/bin/glnxa64 -lmat -lmx

$ ./Test 
Reading file /home/dkumar/CPP_ExampleCodes_DKU/Read_mat_File/Test_FILE.mat...

matread_Matrix 
oDoubleMat_LOC.nRows 3 ; oDoubleMat_LOC.nCols 3 
From main 
oDoubleMat.nRows 3 ; oDoubleMat.nCols 3 
 copied value : 1.000000 
 copied value : 5.000000 
 copied value : 3.000000 
 copied value : 2.000000 
 copied value : 7.000000 
 copied value : 5.000000 
 copied value : 3.000000 
 copied value : 1.000000 
 copied value : 9.000000 
Reading file /home/dkumar/CPP_ExampleCodes_DKU/Read_mat_File/Test_FILE.mat...

 From inside matread_Cell : nRows 3 and nCols 3 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
Reading file /home/dkumar/CPP_ExampleCodes_DKU/Read_mat_File/Test_FILE.mat...

 From inside matread_Cell : nRows 3 and nCols 2 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 
 copied value : 0.000000 

Also, I could not read this field properly as well: Creator = 'DKumar';

UPDATE:

Based on suggestion of @Sherwin

My C-code:

#include <stdio.h>
#include <stdlib.h>
#include "/usr/local/MATLAB/R2011b/extern/include/mat.h"

mxArray *arr;
mxArray *C_CELL;
/* declare  a 2 x 1 array of pointers to access the cell array in C */
mxArray *cellArray[2];

struct stDoubleMat{
   double* pValueInField;
   int nRows, nCols;
};


void matread_Matrix(MATFile* pmat , const char *FieldName2Read, struct stDoubleMat* poDoubleMat_LOC)
{
    // extract the specified variable
    arr = matGetVariable(pmat, FieldName2Read);

    double *pr;
    if (arr != NULL && !mxIsEmpty(arr)) {
        // copy data
        mwSize num = mxGetNumberOfElements(arr);

        pr = mxGetPr(arr);

        if (pr != NULL) {
        poDoubleMat_LOC->pValueInField = pr;
            poDoubleMat_LOC->nRows  = mxGetM(arr);
            poDoubleMat_LOC->nCols  = mxGetN(arr);
        }
    printf("matread_Matrix \n") ;
        printf( "oDoubleMat_LOC.nRows %i ; oDoubleMat_LOC.nCols %i \n", poDoubleMat_LOC->nRows , poDoubleMat_LOC->nCols);

    }else{
        printf("nothing to read \n") ;
    }
    return;
}

void matread_String(MATFile* pmat , const char *FieldName2Read)
{
    // extract the specified variable
    arr = matGetVariable(pmat, FieldName2Read);

    double *pr;
    if (arr != NULL && !mxIsEmpty(arr)) {
        // copy data
        mwSize num = mxGetNumberOfElements(arr);
        pr = mxGetPr(arr);

        if (pr != NULL) {
            char *p2 = (char*) pr; 

            // Printing and checking
        int i2;
        for (i2 = 0; i2 < num; i2++)
        {
        printf(" copied value : %s \n", p2);
        p2 = p2 +1;
        }

    }

    }else{
            printf("nothing to read \n") ;
    }
    return;
}

void matread_Cell(MATFile* pmat , const char *FieldName2Read, int CellIndex)
{

    // extract the specified variable
    C_CELL = matGetVariable(pmat, FieldName2Read);
    cellArray[CellIndex] = mxGetCell(C_CELL, CellIndex);

    double *p2 = (double*) mxGetPr(cellArray[CellIndex]); 
    int nRows  = mxGetM(cellArray[CellIndex]);
    int nCols  = mxGetN(cellArray[CellIndex]);

    printf(" From inside matread_Cell : nRows %i and nCols %i \n", nRows, nCols);

    int i2;
    for (i2 = 0; i2 < nRows*nCols; i2++)
    {
    printf(" copied value : %f \n", *p2);
    p2 = p2 +1;
    }
}


int main(int argc, char **argv)
{
    const char *FileName = "/home/dkumar/CPP_ExampleCodes_DKU/Read_mat_File/Test_FILE.mat";
    const char *FieldName2Read = "A";

    //Open file to get directory
    printf("Reading file %s...\n\n", FileName);
    MATFile* pmat = matOpen(FileName, "r");

    if (pmat == NULL) {
      printf("Error opening file %s\n", FileName);
      return;
    }

    struct stDoubleMat oDoubleMat; 
    matread_Matrix(pmat, FieldName2Read, &oDoubleMat);
    double* v = oDoubleMat.pValueInField;

    int i;
    for (i = 0; i < oDoubleMat.nCols*oDoubleMat.nRows; i++)
    {
        printf(" copied value : %f \n", *v);
        v = v +1;
    }

    // Reading the structure
    const char *FieldName2Read2 = "Filters";
    matread_Cell(pmat, FieldName2Read2, 0);
    matread_Cell(pmat, FieldName2Read2, 1);

    // Reading the string
    const char *FieldName2Read3 = "Creator";
    matread_String(pmat, FieldName2Read3);

    // cleanup the mex-array
    mxDestroyArray(arr);
    mxDestroyArray(C_CELL);

    /* How to delete mxArray of pointer : should this be a array of pointers */
    //mxDestroyArray(cellArray[0]);
    //mxDestroyArray(cellArray[1]);


    // close the file
    matClose(pmat);

    return 0;
}

Output:

oDoubleMat.nRows 3 ; oDoubleMat.nCols 3 
 copied value : 1.000000 
 copied value : 5.000000 
 copied value : 3.000000 
 copied value : 2.000000 
 copied value : 7.000000 
 copied value : 5.000000 
 copied value : 3.000000 
 copied value : 1.000000 
 copied value : 9.000000 
 From inside matread_Cell : nRows 3 and nCols 3 
 copied value : -1.000000 
 copied value : -1.000000 
 copied value : -1.000000 
 copied value : -1.000000 
 copied value : 8.000000 
 copied value : -1.000000 
 copied value : -1.000000 
 copied value : -1.000000 
 copied value : -1.000000 
 From inside matread_Cell : nRows 3 and nCols 2 
 copied value : -2.000000 
 copied value : -2.000000 
 copied value : -2.000000 
 copied value : -2.000000 
 copied value : 16.000000 
 copied value : -2.000000 
 copied value : D 
 copied value :  
 copied value : K 
 copied value :  
 copied value : u 
 copied value :  
 copied value :  

Problem: 1) String value stored in creator is not properly displayed.

2) How to delete cellArray[2])?

回答1:

It turns out with a slight change, your code works:

In the function "void matread_Cell" replace the line double* p2 = (double*)cellArray[CellIndex]; with:

p2 = (double*) mxGetPr(cellArray[CellIndex]); 

I checked it out. It does the work.

Also to read the creator field, a similar code to mtread_matrix should work, just the type is char* instead of double* (I didn't check this one though. let me know if it doesn't work).

update: you can use the following code to read strings. (Reference: here)

void matread_string(const char *file, const char *FieldName2Read, char *pr, mwSize *len)
{
    printf("Reading file %s...\n\n", file);

    //Open file to get directory
    MATFile* pmat = matOpen(file, "r");

    if (pmat == NULL) {
      printf("Error opening file %s\n", file);
      return;
    }

    // extract the specified variable
    arr = matGetVariable(pmat, FieldName2Read);

    if (arr != NULL && !mxIsEmpty(arr)) {
        // copy data
        mwSize num = mxGetNumberOfElements(arr);

        //int mxGetString(const mxArray *pm, char *str, mwSize strlen);
        int res= mxGetString(arr, pr, num+1); //strlen should be len+1. c.f. reference.
        if(res==0)
            printf("success!\n");
        else
            printf("failed.\n");


        if ( pr == NULL){
            printf("null pointer.\n");
        }   
        printf("matread_string \n") ;
        printf( "len: %i \n", (int)num);

        *len=num; 

    }else{
        printf("nothing to read \n") ;
    }
    // close the file
    matClose(pmat);

    return;
}

In the main you can use it like:

 const char *FieldName2Read3 = "Creator";
    char pr[20];
    mwSize len;
    matread_string(FileName, FieldName2Read3, pr, &len);

    //int i;
    printf(" copied value: %s \n",pr);
    for (i = 0; (mwSize) i <  len; i++)
    {
        printf(" copied value : %c \n", pr[i]);
    } 

Regarding deallocating cellArray, I get the error: "pointer being freed was not allocated", so I don't think you need to free that. Another useful command for freeing dynamic memory is : void mxFree(void *ptr);

Regarding the mexPrintf function I am actually able to use it. I just get a warning implicit declaration of function 'mexPrintf' is invalid in C99 [-Wimplicit-function-declaration], since I am compiling via gcc rather than mex. If you are using gcc, you probably need to include the proper libraries to recognize that function. You might find this useful, as it worked for me.



标签: c matlab