viernes, 15 de junio de 2012

Creación de librerias en Matlab para su uso en aplicaciones en C

Estos días estoy trabajando en la integración de una aplicación creada en Matlab (2008b) junto con otra en una aplicación en C\C++ en Visual Studio (2005) y la verdad es que aunque a primer vista parezca un proceso fácil en realidad es algo bastante enrevesado.

A lo largo de este tutorial trabajaremos con el siguiente código Matlab que debemos guardar en un archivo llamado getCorrelation.m y que calcula la coeficiente de correlación entre dos matrices (que indica si son o no similares las diferencias entre sus valores):

function cor = getCorrelation(temp, comp)
    tsize = size(temp);
    csize = size(comp);
   
    if (tsize == csize)   
        %media de los valores de la matriz temp 
        aver_temp = median(median(temp)); 
        %media de los valores de la matriz comp
        aver_comp = median(median(comp));
       
        tc = 0.0;
        vaa = 0.0;
        vab = 0.0;
        for nr=1:tsize(1)
            for nc=1:tsize(2)
                tc = tc + double((temp(nr, nc)-aver_temp) * (comp(nr, nc) - aver_comp));
                vaa = vaa + double((temp(nr, nc)-aver_temp)^2);
                vab = vab + double((comp(nr, nc)-aver_comp)^2);       
            end
        end      
        % Calculo de la fórmula completa.
        cor = tc/sqrt(double(vaa * vab));
    end 


Antes de crear una librería en C de este código desde Matlab debemos configurarlo para que compile utilizando el compilador de Visual Studio para lo que se debe utilizar el siguiente comando en Matlab:

>> mbuild -setup

Tras lo cual se nos preguntara si queremos cambiar el compilador y que lo seleccionemos de una lista de entre los disponibles en el sistema. Como trabajaremos con VS2005 sera este el que se debe elegir. Este paso genera un script de configuración que por lo menos en mi caso no ha resultado funcionar por defecto por lo que lo debemos editar modificando un par de lineas. Para ello buscamos el archivo compopts.bat (que se encuentra en la carpeta de configuración de aplicaciones del sistema algo similar a C:\Documents and Settings\TU_USER\Datos aplicaciones\MathWorks\MATLAB\R2008b). En este archivo debemos modificar las siguientes asignaciones de variables:

set VSINSTALLDIR=C:\Program Files\Microsoft Visual Studio 8   
(O donde este instalada vuestra versión de VS)

set INCLUDE=%VCINSTALLDIR%\ATLMFC\INCLUDE;%VCINSTALLDIR%\INCLUDE;%LINKERDIR%\INCLUDE;%VSINSTALLDIR%\SDK\v2.0\include;%VCINSTALLDIR%\PlatformSDK\Include;%INCLUDE%

set LIB=%VCINSTALLDIR%\ATLMFC\LIB;%VCINSTALLDIR%\LIB;%LINKERDIR%\lib;%VSINSTALLDIR%\SDK\v2.0\lib;%MATLAB%\extern\lib\win32;%VCINSTALLDIR%\PlatformSDK\Lib;%LIB%


Ahora ya podemos generar la librería sin problemas y con solo escribir en Matlab el comando:

>> mcc -B csharedlib:libcorrelation getCorrelation.m

Que generará entre otro, los siguientes ficheros que son los que utilizaremos después para crear la aplicación que utilizará esta librearía desde Visual Studio:

libcorrelation.c
libcorrelation.h
libcorrelation.dll
libcorrelation.lib

Ahora ya podemos olvidarnos de Matlab y comenzar a trabajar en Visual Studio. En primer lugar debemos configurar nuestro proyecto para lo que debemos indicar donde se encuentran los archivos creados y las cabeceras y librerías de Matlab que normalmente estarán en los directorios:

...Caperta instalacion\MATLAB\R2008b\extern\lib\win32\microsoft
...Carpeta instalacion\MATLAB\R2008b\extern\include

También deberemos añadir al proyecto las librerías:

mclmcrrt.lib
libcorrelation.lib

Ahora ya podemos incluir en nuestro código la definición de nuestra librería creada en Matlab y las cabeceras de la API de Matlab:

#include "libcorrelation.h"

Y el resto del código, que debe empezar por la inicialización de la API de Matlab para lo que debemos llamar a la función mclInitializeApplication y a la función de inicialización que se ha definido en la librería que vendrá siempre dada por el nombre de la librería junto con la palabra Initialize (en nuestro caso la función libcorrelationInitialize). Por tanto el código de inicialización queda definido por:

if (!mclInitializeApplication(NULL,0)) {
        fprintf(stderr,"No se ha podido inicializar la API de Matlab!\n");
        return;
}
if (!libcorrelationInitialize()){
        fprintf(stderr,"No se ha podido inicializar la librería!\n");
        return;
}

Ahora ya podemos definir las matrices (por medio de la estructura mxArray) con los datos que le pasaremos a la función definida en la librería para que realice el cálculo del coeficiente de correlación así como la matriz en la que se almacenara la salida producida. Para ello debemos escribir:

    // Definición estructuras y datos
    mxArray *in1, *in2;
    mxArray *out = NULL;
    double data1[] = {1,2,3,4,5,6,7,8,9};
    double data2[] = {1,1,1,3,3,3,6,6,6};
    // creación matrices de entrada entrada    
    in1 = mxCreateDoubleMatrix(3,3,mxREAL);
    in2 = mxCreateDoubleMatrix(3,3,mxREAL);
    // Asignación de valores.
    memcpy(mxGetPr(in1), data1, 9*sizeof(double));
    memcpy(mxGetPr(in2), data2, 9*sizeof(double));
 
A continuación ya podemos  llamar a nuestra función que se habrá generado con el mismo nombre con el que está definida en el código en Matlab precedida del prefijo mlf siendo los primeros argumentos el numero de argumentos de salida y las estructuras donde se almacenan y el resto los definidos en la cabecera de la función del código en Matlab:

    mlfGetCorrelation(1, &out, in1, in2);
   
Aunque en este caso obtenemos una matriz de salida de 1x1, si quisiéramos saber sus dimensiones podemos escribir (dónde mwSize es en realidad un size_t):

    mwSize nRow = mxGetM(out); //Filas
    mwSize nCol = mxGetN(out); //columnas

Para recibir los datos de forma que podamos utilizarlos desde C utilizaremos:

    double *xValues = mxGetPr(out);
   
Y ya podemos imprimir la solución (o soluciones si se tratase de una matriz de mayor dimensión):

    for(size_t row = 0; row < nRow; row++) {
        for(size_t col = 0; col < nCol; col++) {
            cout << xValues[nRow * col + row];
        }
        printf("\n");
    }
 
Finalmente para liberar las estructuras generadas utilizaremos las función mxDetstroyArray:

    mxDestroyArray(in1); in1=0;
    mxDestroyArray(in2); in2=0;
    mxDestroyArray(out); out=0;
 
Y al igual que en la inicialización deberemos ejecutar las funciones de terminación en primer lugar la de la librería que vendrá definida por el nombre de la librería seguido de la palabra Terminate y a continuación la de la API de Matlab que viene dada por la función mclTerminateApplication:

    libcorrelationTerminate();
    mclTerminateApplication();


Por ultimo os dejo un par de enlaces donde podéis encontrar mas ejemplos de código C utilizando Matlab e información adicional:


No hay comentarios:

Publicar un comentario