Utilisation d'OpenGL dans Matlab pour obtenir un tampon de profondeur
Question
J'ai déjà posé une question similaire et je n'ai pas réussi à trouver une réponse directe .
Quelqu'un pourrait-il fournir un exemple de code pour extraire le tampon de profondeur du rendu d'un objet dans une figure dans Matlab?
Disons donc que je charge un fichier obj ou même juste un simple appel de surf, le rend et que je veux maintenant accéder à sa mémoire tampon de profondeur, quel code le fera pour moi en utilisant à la fois Matlab et OpenGL.C'est à dire.comment configurer cela et accéder aux données réelles?
Je souhaite essentiellement pouvoir utiliser les puissantes fonctions de traçage de Matlabs, puis pouvoir accéder au contexte graphique sous-jacent pour extraire le tampon de profondeur.
REMARQUE: La prime spécifie JOGL mais ce n'est pas un must.Tout code qui agit comme ci-dessus et peut me fournir le tampon de profondeur après l'avoir exécuté dans Matlab est suffisant)
La solution
Aujourd'hui, je suis allé boire avec mes collègues, et après cinq bières et quelques tequillas, j'ai trouvé cette question et je me suis dit "à vous!" J'ai donc eu du mal pendant un moment, mais j'ai trouvé une solution simple en utilisant MEX. J'ai émis l'hypothèse que le contexte OpenGL, créé par la dernière fenêtre, pouvait rester actif et donc être accessible à partir de "C", si le script s'exécutait dans le même fil.
J'ai créé un simple programme "C" qui appelle une fonction matlab, appelée "testofmyfilter" qui trace la réponse en fréquence d'un filtre (c'était le seul script que j'avais sous la main). Ceci est rendu en utilisant OpenGL. Ensuite, le programme utilise glGetViewport () et glReadPixels () pour accéder aux tampons OpenGL. Ensuite, il crée une matrice, la remplit avec les valeurs de profondeur et la transmet à la deuxième fonction, appelée "trytodisplaydepthmap". Il affiche simplement la carte de profondeur en utilisant la fonction imshow. Notez que la fonction MEX est également autorisée à renvoyer des valeurs, donc peut-être que le post-traitement ne devrait pas être une autre fonction, mais je ne suis pas en état de comprendre comment cela se fait. Cela devrait être trivial, cependant. Je travaille avec MEX pour la première fois aujourd'hui.
Sans plus attendre, j'ai utilisé des codes sources:
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
Enregistrez tout cela dans le même répertoire, compilez test.c avec (tapez-le sur la console Matlab):
mex test.c Q:\MATLAB\R2008a\sys\lcc\lib\opengl32.lib
Où "Q: \ MATLAB \ R2008a \ sys \ lcc \ lib \ opengl32.lib" est le chemin du fichier "opengl32.lib".
Et enfin, exécutez tout cela en tapant simplement "test" dans la console matlab. Il devrait faire apparaître une fenêtre avec la réponse en fréquence du filtre et une autre fenêtre avec le tampon de profondeur. Notez que les tampons avant et arrière sont échangés au moment où le code "C" lit le tampon de profondeur, il peut donc être nécessaire d'exécuter le script deux fois pour obtenir des résultats (donc le tampon avant qui contient maintenant les résultats échange à nouveau avec le tampon arrière, et la profondeur peut être lue). Cela pourrait être fait automatiquement par "C", ou vous pouvez essayer d'inclure getframe (gcf); à la fin de votre script (qui lit également OpenGL pour qu'il échange les tampons pour vous, ou quelque chose du genre).
Cela fonctionne pour moi dans Matlab 7.6.0.324 (R2008a). Le script s'exécute et crache ce qui suit:
>>test
GL_VIEWPORT = [0, 0, 560, 419]
0x11150020 0x0bd39620 0x12b20030 419
Et bien sûr, il affiche les images. Notez que la plage de tampon de profondeur dépend de Matlab et peut être assez élevée, donc donner un sens aux images générées peut ne pas être simple.
Autres conseils
le porc est la bonne. Voici une version légèrement formatée et plus simple qui est multiplateforme.
Créez un fichier appelé 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;
}
Ensuite, si vous êtes sur Windows, compilez en utilisant
mex mexGetDepth.c "path to OpenGL32.lib"
ou si vous êtes sur un système nix
mex mexGetDepth.c "path to opengl32.a"
Ensuite, exécutez le petit script suivant pour tester la nouvelle fonction
peaks;
figure(1);
depthData=mexGetDepth;
figure
imshow(depthData);