在Matlab中使用OpenGL获取深度缓冲区

10

我之前曾经问过类似的问题,但没有找到直接的答案。有人能提供样例代码,在Matlab中从对象渲染的深度缓冲区中提取数据并将其呈现在图像上吗?

假设我已经加载了一个obj文件或者只是一个简单的surf函数调用,并进行了渲染,那么如何使用Matlab和OpenGL获得深度缓冲区数据的代码是什么?也就是说,我该如何设置并访问实际数据?

本质上,我想要使用Matlab强大的绘图函数,并能够访问底层的图形环境以获取深度缓冲区。

注意:悬赏指定了JOGL,但这不是必须的。任何能够执行以上操作并在Matlab中提供深度缓冲区的代码都足够。


1
你可以尝试提供赏金来增加问题的关注度。 - PeterT
我本意是要发问的,但当我发布问题后才发现需要等待两天,直到当前问题符合条件。 - twerdster
你接受仅涉及Matlab的答案吗?我对JOGL一无所知。 - Andrey Rubshtein
1
是的,这个问题特别涉及Matlab。如果我们能够有效地访问Matlab图中底层的OpenGL层,那么我们就有很多事情要做,因此Matlab至关重要。 - twerdster
2个回答

14

深度图

今天,我和同事们一起喝酒,喝了五杯啤酒和一些龙舌兰后,我发现了这个问题,并想着“来试试吧!”我挣扎了一段时间,但后来我使用了MEX找到了一个简单的解决方案。我假设由最后一个窗口创建的OpenGL上下文可能仍然处于活动状态,因此如果脚本在同一线程中运行,则可以从“C”中访问。

我创建了一个简单的“C”程序,调用一个名为“testofmyfilter”的matlab函数,该函数绘制滤波器的频率响应(这是我手头唯一的脚本)。这是使用OpenGL渲染的。然后程序使用glGetViewport()和glReadPixels()来获取OpenGL缓冲区。然后它创建一个矩阵,填充深度值,并将其传递给第二个函数,称为“trytodisplaydepthmap”。它只是使用imshow函数显示深度图。请注意,MEX函数也可以返回值,因此后处理可能不需要另一个函数,但我现在无法理解如何完成。不过应该很简单。今天是我第一次使用MEX。

没有更多的延迟,这是我使用的源代码:

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

请将所有这些内容保存到同一个目录中,使用以下命令在Matlab控制台中编译test.c文件:

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

"Q:\MATLAB\R2008a\sys\lcc\lib\opengl32.lib" 是 "opengl32.lib" 文件的路径。

最后,在Matlab控制台中键入 "test" 即可执行所有操作。它应该会弹出一个带有滤波器频率响应的窗口和另一个深度缓冲区窗口。请注意,前缓冲区和后缓冲区在 “C” 代码读取深度缓冲区时被交换,所以可能需要运行脚本两次才能获得任何结果(这样现在包含了结果的前缓冲区就再次交换到了后缓冲区中,然后可以读出深度)。这可以由 “C” 自动完成,或者您可以尝试在脚本末尾包括 getframe(gcf);(它也从OpenGL中读取,因此可以自动为您交换缓冲区,或其他操作)。

这在 Matlab 7.6.0.324 (R2008a) 中对我有效。该脚本运行并输出以下内容:

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

当然,它可以显示图像。请注意深度缓冲区范围取决于Matlab,并且可能非常高,因此理解生成的图像可能并不直观。


哦,我错过了要求。当然,这也适用于surf()。只是在http://www.mathworks.com/help/techdoc/ref/surf.html上尝试了一下代码,并得到了:http://www.luki.webzdarma.cz/up/depthmap.png 很明显。 - the swine
3
你值得拥有一枚奖章、再来一杯啤酒和300点声望。 - twerdster
1
我能否建议您将所有内容整合起来,制作成一个漂亮的MathWorks提交?不仅我一个人会发现这很有用。 - twerdster
对于任何想要测试这个的人,我建议将testofmyfilter.m的整个内容替换为简单的“peaks”。 - twerdster
你好,非常感谢所有的赞美。我不太擅长matlab,我大多数时候都是用mspaint或“C”来做我的数学。所以...如果你认为有人可能会觉得这很有用,欢迎你自己提交。我不需要任何荣誉。 - the swine
显示剩余2条评论

2

the swine的答案是正确的。下面是一个稍微格式化且跨平台的简化版本。

创建一个名为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;
}

如果您使用的是Windows操作系统,则需要使用以下方法进行编译:

mex mexGetDepth.c "path to OpenGL32.lib"

或者如果您在Nix系统上
mex mexGetDepth.c "path to opengl32.a"

然后运行以下小脚本来测试新功能

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

我们需要在某个地方声明 glGetIntegerv 和 glReadPixels 函数吗?在原始代码中,使用 extern WINAPI void... 进行了声明,但我不确定在 Linux 中应该如何处理。 - slam_duncan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接