Optimization 在像素着色器中实现卷积过滤器的最有效方法是什么?

Optimization 在像素着色器中实现卷积过滤器的最有效方法是什么?,optimization,opengl,glsl,shader,Optimization,Opengl,Glsl,Shader,在像素着色器中实现卷积的代价有点高,因为纹理获取的数量非常多 实现卷积滤波器的一种直接方法是对每个片段使用两个for循环进行N x N查找。一个简单的计算表明,使用4x4高斯核模糊的1024x1024图像需要1024x102x4x4=16M查找 对此我们能做些什么 可以使用一些需要较少查找的优化吗?我对特定于内核的优化不感兴趣,比如针对高斯函数的优化(或者它们是特定于内核的?) 至少可以通过某种方式利用像素的局部性来加快这些查找吗 谢谢 高斯核是可分离的,这意味着您可以先进行水平传递,然后进行垂

在像素着色器中实现卷积的代价有点高,因为纹理获取的数量非常多

实现卷积滤波器的一种直接方法是对每个片段使用两个for循环进行N x N查找。一个简单的计算表明,使用4x4高斯核模糊的1024x1024图像需要
1024x102x4x4=16M
查找

对此我们能做些什么

  • 可以使用一些需要较少查找的优化吗?我对特定于内核的优化不感兴趣,比如针对高斯函数的优化(或者它们是特定于内核的?)
  • 至少可以通过某种方式利用像素的局部性来加快这些查找吗

  • 谢谢

    高斯核是可分离的,这意味着您可以先进行水平传递,然后进行垂直传递(或者反过来)。把O(N^2)变成O(2N)。这适用于所有可分离的过滤器,不仅仅适用于模糊(并非所有过滤器都是可分离的,但许多过滤器是可分离的,有些过滤器“与其他过滤器一样好”)

    或者,在模糊过滤器(高斯或非高斯)的特定情况下,这都是一种“加权和”,您可以利用纹理插值,这对于较小的内核大小可能更快(但对于较大的内核大小肯定不是)

    编辑:“线性插值”方法的图像

    编辑(根据Jerry Coffin的要求)以总结评论:

    在“纹理过滤器”方法中,线性插值将根据样本位置到纹理中心的反向距离生成相邻纹理的加权和。这是由纹理硬件免费完成的。这样,16个像素可以在4次抓取中求和。除了分离内核外,还可以利用纹理过滤

    在示例图像中,在左上角,您的样本(圆)击中了纹理的中心。你得到的和“最近的”过滤一样,你得到的是texel的值。在右上角,你在两个纹理之间,你得到的是它们之间的50/50个平均值(蓝色的更亮的着色图)。在右下角,采样范围在4 texel之间,但略接近左上角。这将为您提供所有4个的加权平均值,但权重偏向左上角的一个(最深的蓝色阴影)

    以下建议由datenwolf提供(见下文):


    “我想建议的另一种方法是在傅里叶空间中操作,在这里卷积变成傅里叶变换信号和傅里叶变换核的简单乘积。尽管GPU上的傅里叶变换本身实现起来相当繁琐,至少使用OpenGL着色器是如此。但在OpenCL中很容易实现。实际上,我用OpenCL实现了这些东西,现在,我的3D引擎中的很多图像处理都是在OpenCL中进行的

    OpenCL是专门为在GPU上运行而设计的。快速傅立叶变换实际上是维基百科OpenCL文章中的一段示例代码:en.Wikipedia.org/wiki/OpenCL,是的,性能提升是巨大的。FFT执行时最多为O(n logn),反之亦然。滤波器核傅里叶表示可以预先计算。方法是FFT->multiply with kernel->IFFT,它归结为O(n+2n log n)操作。注意,实际的卷积是O(n)

    在可分离、有限卷积(如高斯模糊)的情况下,分离解将优于傅里叶方法。但对于广义的、可能的不可分离核,傅里叶方法可能是最快的方法。
    OpenCL与OpenGL很好地集成,例如,您可以将OpenGL缓冲区(纹理和顶点)用于OpenCL程序的输入和输出。“

    罗托格卢普对我的问题的回答值得一读;特别是,它确实帮助我理解了可分离滤波器的概念。

    高斯滤波器不仅是可分离的,而且在O(1)中也是可计算的:

    有类似于Deriche的递归计算:


    另一种方法是使用逐步函数逼近高斯曲线:(我想当然也可以使用分段线性函数)。

    为了详细说明“纹理过滤器”方法,您可以将4x4块中的所有16个像素相加/平均,只需在纹理之间放置4个样本。对于框模糊,这完全是微不足道的,只需将它们放在中间即可。对于高斯分布,它有点复杂,轻拍需要靠近中心一点,这样这些像素就有更多的权重(尽管我现在手头没有确切的距离)。总的来说,分离内核需要4次而不是8次,非优化版本需要16次。线性插值有助于避免对纹理进行采样,从而精确命中一个纹理,您可以将采样点(“点击”)放置在两者之间。插值硬件将返回一个加权平均值(通过到相邻texel中心的反向距离加权),而无需额外成本。我会试着画一张图,这可能更容易。@Albus Dumbledore:高斯分布是一个连续的,可微的分布。对于离散滤波过程,该轮廓必须转化为离散元素的有限向量。人们可以称之为样本,但通常指的是过滤器的样本。所以在内核上,它会点击滤波器的轮廓。我想建议的另一种方法是在傅里叶空间中操作,在这里卷积变成傅里叶变换信号和傅里叶变换内核的简单乘积。尽管GPU上的傅里叶变换本身实现起来相当繁琐,至少使用OpenGL着色器是如此。但在OpenCL中很容易实现。实际上,我用OpenCL实现了这些东西,现在,我的3D引擎中的很多图像处理都是在OpenCL中进行的。添加了一个图像,希望能帮助理解。在左上角,是您的样品(