2012-01-14 11 views
10

He hecho una pregunta similar antes y no he podido encontrar un answer directo.Uso de OpenGL en Matlab para obtener el búfer de profundidad

¿Podría alguien proporcionar un código de muestra para extraer el búfer de profundidad de la representación de un objeto en una figura en Matlab?

Digamos que cargué un archivo OBJ o incluso una simple llamada de navegación, la traduzco y ahora quiero llegar a su búfer de profundidad, entonces, ¿qué código hará eso para mí usando Matlab y OpenGL? Es decir. ¿Cómo configuro esto y luego accedo a los datos reales?

Básicamente, quiero poder usar las potentes funciones de trazado de Matlabs y luego poder acceder al contexto de gráficos subyacente para sacar el búfer de profundidad.

NOTA: El bounty especifica JOGL, pero eso no es obligatorio. Cualquier código que actúa como antes y me puede proporcionar el buffer de profundidad después de correr en Matlab es suficiente)

+1

siempre puede tratar de ofrecer una recompensa para aumentar la atención que recibe una pregunta. – PeterT

+0

Tengo la intención pero solo descubrí cuando publiqué la pregunta que necesito esperar dos días hasta que esta pregunta actual sea elegible. – twerdster

+0

¿Aceptas respuestas que involucren solo a Matlab? No tengo idea sobre JOGL. –

Respuesta

13

depthmap thing

Hoy en día, fui a beber con mis colegas, y después de cinco cervezas y algunos tequillas he encontrado esto pregunta y pensamiento, "¡ya te tengo!" Así que estuve luchando por un tiempo, pero luego encontré una solución simple usando MEX. Teoricé que el contexto de OpenGL, creado por la última ventana, podría permanecer activo y, por lo tanto, podría accederse desde "C", si el script se ejecutó en el mismo hilo.

Creé un sencillo programa "C" que llama a una función de matlab, llamada "testofmyfilter" que traza la respuesta de frecuencia de un filtro (que era el único script que tenía a mano). Esto se representa usando OpenGL. Luego, el programa usa glGetViewport() y glReadPixels() para acceder a los búferes de OpenGL. Luego crea una matriz, la rellena con los valores de profundidad y la pasa a la segunda función, llamada "trytodisplaydepthmap". Simplemente muestra el mapa de profundidad con la función imshow. Tenga en cuenta que la función MEX también puede devolver valores, por lo que tal vez el postprocesamiento no tenga que ser otra función, pero no estoy en condiciones de poder entender cómo se hace. Sin embargo, debería ser trivial. Estoy trabajando con MEX por primera vez hoy.

Sin más demora, hay códigos fuente I utilizados:

testofmyfilter.m

imp = zeros(10000,1); 
imp(5000) = 1; 
% impulse 

[bwb,bwa] = butter(3, 0.1, 'high'); 
b = filter(bwb, bwa, imp); 
% filter impulse by the filter 

fs = 44100; % sampling frequency (all frequencies are relative to fs) 
frequency_response=fft(b); % calculate response (complex numbers) 
amplitude_response=20*log10(abs(frequency_response)); % calculate module of the response, convert to dB 
frequency_axis=(0:length(b)-1)*fs/length(b); % generate frequency values for each response value 
min_f=2; 
max_f=fix(length(b)/2)+1; % min, max frequency 

figure(1); 
lighting gouraud 
set(gcf,'Renderer','OpenGL') 

semilogx(frequency_axis(min_f:max_f),amplitude_response(min_f:max_f),'r-') % plot with logarithmic axis using red line 
axis([frequency_axis(min_f) frequency_axis(max_f) -90 10]) % set axis limits 

xlabel('frequency [Hz]'); 
ylabel('amplitude [dB]'); % legend 

grid on % draw grid 

test.c

//You can include any C libraries that you normally use 
#include "windows.h" 
#include "stdio.h" 
#include "math.h" 
#include "mex.h" //--This one is required 

extern WINAPI void glGetIntegerv(int n_enum, int *p_value); 

extern WINAPI void glReadPixels(int  x, 
    int  y, 
    int  width, 
    int  height, 
    int  format, 
    int  type, 
    void *  data); 

#define GL_VIEWPORT      0x0BA2 
#define GL_DEPTH_COMPONENT    0x1902 
#define GL_FLOAT       0x1406 

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 
{ 
    int viewport[4], i, x, y; 
    int colLen; 
    float *data; 
    double *matrix; 
    mxArray *arg[1]; 

    mexCallMATLAB(0, NULL, 0, NULL, "testofmyfilter"); 
    // call an .m file which creates OpenGL window and draws a plot inside 

    glGetIntegerv(GL_VIEWPORT, viewport); 
    printf("GL_VIEWPORT = [%d, %d, %d, %d]\n", viewport[0], viewport[1], viewport[2], viewport[3]); 
    // print viewport dimensions, should be [0, 0, m, n] 
    // where m and n are size of the GL window 

    data = (float*)malloc(viewport[2] * viewport[3] * sizeof(float)); 
    glReadPixels(0, 0, viewport[2], viewport[3], GL_DEPTH_COMPONENT, GL_FLOAT, data); 
    // alloc data and read the depth buffer 

    /*for(i = 0; i < 10; ++ i) 
     printf("%f\n", data[i]);*/ 
    // debug 

    arg[0] = mxCreateNumericMatrix(viewport[3], viewport[2], mxDOUBLE_CLASS, mxREAL); 
    matrix = mxGetPr(arg[0]); 
    colLen = mxGetM(arg[0]); 
    printf("0x%08x 0x%08x 0x%08x %d\n", data, arg[0], matrix, colLen); // debug 
    for(x = 0; x < viewport[2]; ++ x) { 
     for(y = 0; y < viewport[3]; ++ y) 
      matrix[x * colLen + y] = data[x + (viewport[3] - 1 - y) * viewport[2]]; 
    } 
    // create matrix, copy data (this is stupid, but matlab switches 
    // rows/cols, also convert float to double - but OpenGL could have done that) 

    free(data); 
    // don't need this anymore 

    mexCallMATLAB(0, NULL, 1, arg, "trytodisplaydepthmap"); 
    // pass the array to a function (returnig something from here 
    // is beyond my understanding of mex, but should be doable) 

    mxDestroyArray(arg[0]); 
    // cleanup 

    return; 
} 

trytodisplaydepthmap.m:

function [] = trytodisplaydepthmap(depthMap) 

figure(2); 
imshow(depthMap, []); 
% see what's inside 

Guarde todo de th ESE en el mismo directorio, compilar test.c con (tipo que a la consola de Matlab):

mex test.c Q:\MATLAB\R2008a\sys\lcc\lib\opengl32.lib 

donde "Q: \ MATLAB \ R2008a \ sys \ lcc \ lib \ opengl32.lib" es el camino a "opengl32 archivo .lib ".

Y finalmente ejecútelo simplemente escribiendo "test" en la consola de matlab. Debería mostrar una ventana con respuesta de frecuencia de filtro y otra ventana con el búfer de profundidad. Tenga en cuenta que los búferes anterior y posterior se intercambian en el momento en que el código "C" lee el búfer de profundidad, por lo que puede ser necesario ejecutar el script dos veces para obtener resultados (por lo que el búfer frontal que ahora contiene los intercambios se intercambia con búfer de nuevo, y la profundidad se puede leer).Esto se puede hacer automáticamente con "C", o puede intentar incluir getframe (gcf); al final de su script (que también se lee desde OpenGL para que intercambie los buffers por usted, o algo así).

Esto funciona para mí en Matlab 7.6.0.324 (R2008a). La secuencia de comandos se ejecuta y escupe lo siguiente:

>>test 
GL_VIEWPORT = [0, 0, 560, 419] 
0x11150020 0x0bd39620 0x12b20030 419 

Y, por supuesto, muestra las imágenes. Tenga en cuenta que el rango del búfer de profundidad depende de Matlab, y puede ser bastante alto, por lo que tener sentido de las imágenes generadas puede no ser sencillo.

+0

oh, me perdí los requisitos. por supuesto, esto también funciona con surf(). Acabo de probar el código en: http://www.mathworks.com/help/techdoc/ref/surf.html y obtuve: http://www.luki.webzdarma.cz/up/depthmap.png el obvio –

+3

que se merece una medalla, otra cerveza y los 300 rep. – twerdster

+1

¿Te puedo recomendar que pongas todo junto y lo conviertas en un buen trabajo de mathworks? No soy el único que lo encuentra útil. – twerdster

2

the swine La respuesta es la correcta. Aquí hay una versión ligeramente formateada y más simple que es multiplataforma.

Crear un archivo llamado mexGetDepth.c

#include "mex.h" 

#define GL_VIEWPORT      0x0BA2 
#define GL_DEPTH_COMPONENT    0x1902 
#define GL_FLOAT       0x1406 

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 
{ 
    int viewport[4], i, x, y; 
    int colLen; 
    float *data; 
    double *matrix; 

    glGetIntegerv(GL_VIEWPORT, viewport); 
    data = (float*)malloc(viewport[2] * viewport[3] * sizeof(float)); 
    glReadPixels(0, 0, viewport[2], viewport[3], GL_DEPTH_COMPONENT, GL_FLOAT, data); 

    plhs[0] = mxCreateNumericMatrix(viewport[3], viewport[2], mxDOUBLE_CLASS, mxREAL); 
    matrix = mxGetPr(plhs[0]); 
    colLen = mxGetM(plhs[0]); 

    for(x = 0; x < viewport[2]; ++ x) { 
     for(y = 0; y < viewport[3]; ++ y) 
      matrix[x * colLen + y] = data[x + (viewport[3] - 1 - y) * viewport[2]]; 
    } 

    free(data); 
    return; 
} 

Entonces, si usted está en las ventanas de compilación usando

mex mexGetDepth.c "path to OpenGL32.lib" 

o si usted está en un sistema nix

mex mexGetDepth.c "path to opengl32.a" 

A continuación, ejecute el siguiente pequeño script para probar la nueva función

peaks; 
figure(1); 
depthData=mexGetDepth; 
figure 
imshow(depthData); 
+0

¿tenemos que declarar las funciones glGetIntegerv y glReadPixels en algún lugar? En el código original, se hace usando el WINAPI void externo ..., pero no estoy seguro de cómo lo haríamos en Linux –

Cuestiones relacionadas