在 StackOverflow 的其他地方,有人问了一个关于 depthbuffer histogram - Create depth buffer histogram texture with GLSL的问题。
我正在编写一个 iOS 图像处理应用程序,对这个问题很感兴趣,但对提供的答案不清楚。那么,是否可以通过 GLSL 使用 GPU 创建图像直方图?
在 StackOverflow 的其他地方,有人问了一个关于 depthbuffer histogram - Create depth buffer histogram texture with GLSL的问题。
我正在编写一个 iOS 图像处理应用程序,对这个问题很感兴趣,但对提供的答案不清楚。那么,是否可以通过 GLSL 使用 GPU 创建图像直方图?
是的,有,尽管它在 iOS 上比你想象的更具挑战性。这是一个完全在 GPU 上生成和绘制的红色直方图,针对实时视频源运行:
汤米在您链接的问题中的建议是一个很好的起点,Scheuermann 和 Hensley 的这篇论文也是如此。建议使用散射为图像中的颜色通道建立直方图。散射是一个过程,您将点网格传递给顶点着色器,然后让该着色器读取该点的颜色。然后将该点所需颜色通道的值写为 X 坐标(Y 和 Z 坐标为 0)。然后,您的片段着色器在目标中的该坐标处绘制一个半透明的 1 像素宽的点。
该目标是一个 1 像素高、256 像素宽的图像,每个宽度位置代表一个颜色箱。通过写出具有低 Alpha 通道(或低 RGB 值)的点,然后使用加法混合,您可以根据特定颜色值在图像中出现的次数为每个 bin 累积更高的值。然后可以读取这些直方图像素以供以后处理。
在 iOS 上的着色器中执行此操作的主要问题是,尽管有相反的报道,Apple 明确指出顶点着色器中的纹理读取在 iOS 上不起作用。我在我所有的 iOS 5.0 设备上都试过这个,但没有一个能够在顶点着色器中执行纹理读取(屏幕只是变黑,没有抛出 GL 错误)。
为了解决这个问题,我发现我可以读取输入图像的原始像素(通过glReadPixels()
或更快的纹理缓存)并将这些字节作为 GL_UNSIGNED_BYTE 类型的顶点数据传递。下面的代码实现了这一点:
glReadPixels(0, 0, inputTextureSize.width, inputTextureSize.height, GL_RGBA, GL_UNSIGNED_BYTE, vertexSamplingCoordinates);
[self setFilterFBO];
[filterProgram use];
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE);
glEnable(GL_BLEND);
glVertexAttribPointer(filterPositionAttribute, 4, GL_UNSIGNED_BYTE, 0, (_downsamplingFactor - 1) * 4, vertexSamplingCoordinates);
glDrawArrays(GL_POINTS, 0, inputTextureSize.width * inputTextureSize.height / (CGFloat)_downsamplingFactor);
glDisable(GL_BLEND);
在上面的代码中,您会注意到我使用一个步幅仅对图像像素的一小部分进行采样。这是因为您可以写出的最低不透明度或灰度级别是 1/256,这意味着一旦该图像中超过 255 个像素具有该颜色值,每个 bin 就会被最大化。因此,我不得不减少处理的像素数,以便将直方图的范围带入这个有限的窗口。我正在寻找一种方法来扩展这个动态范围。
用于执行此操作的着色器如下,从顶点着色器开始:
attribute vec4 position;
void main()
{
gl_Position = vec4(-1.0 + (position.x * 0.0078125), 0.0, 0.0, 1.0);
gl_PointSize = 1.0;
}
并使用片段着色器完成:
uniform highp float scalingFactor;
void main()
{
gl_FragColor = vec4(scalingFactor);
}
可以在我的开源GPUImage框架中找到它的工作实现。抓取并运行 FilterShowcase 示例以查看直方图分析和自己的绘图。
此实现存在一些性能问题,但这是我能想到的在 iOS 上在 GPU 上执行此操作的唯一方法。我愿意接受其他建议。
是的。这显然不是最好的方法,但它确实是 iOS 中最好的方法,因为不支持 OpenCL。您将失去优雅,您的代码可能不会那么简单,但几乎所有 OpenCL 功能都可以通过着色器实现。
如果有帮助,DirectX11 附带了一个用于计算着色器的 FFT 示例。请参阅 DX11 八月 SDK 发行说明。