首页 > 解决方案 > 在 GLSL 中寻找有效的模式过滤器

问题描述

我想在 GLSL 中实现一个称为模式过滤器的图像过滤器,以便在 WebGL 应用程序中使用。模式过滤器从周围像素计算模式(最频繁)值,如果有一个最频繁的值,则将像素颜色设置为模式,否则保持不变。周围像素由内核大小决定,通常为以相关像素为中心的 9 或 25 个像素。

我是着色器、GLSL 和实现性能的复杂性的真正初学者,我目前的实现似乎特别糟糕。我正在寻找一些帮助以找到更有效的方法。

我将当前的着色器放入https://www.shadertoy.com/view/wsVSWw的着色器玩具中,我的速度低于 10 fps!请注意,您需要为 iChannel0 选择一些内容才能看到效果。

编辑

我将计数排序算法转换为 GLSL 并从中值滤波器中抓取一个循环,结果现在是 30fps,新版本位于https://www.shadertoy.com/view/tsVSDm

在我的特殊情况下,我正在处理一张灰度照片,所以我只关心红色通道。

这是为 webgl 实现的着色器,它与 shadertoy 版本有细微差别(例如texture2D(),而不是texture()):

    precision mediump float;
    varying vec2 v_coord;
    uniform sampler2D u_texture;
    uniform vec2 imageResolution;

    int histogram[256];

    void main() {

        vec2 pos = vec2(v_coord.x, 1.0 - v_coord.y);
        vec4 outColor = texture2D(u_texture, pos);

        gl_FragColor = outColor;

        /* the mode filter does not apply to semi-transparent pixels
           for performance reasons, we would need to mix pixels based
           on alpha to get the effective color for the mode calculation
           then remix to get the correct color with the current pixel's
           alpha.  This is both hairy and would add processing time
        */
        if (outColor.a < 1.0) {
            return;            
        }

        vec2 onePixel = vec2(1) / imageResolution;

        for (int i = 0; i<256; i++) {
            histogram[i] = 0;
        }

        int maxValue = 0;
        int offset = int(float(${kernelSize})/2.0);
        for (int yy = 0; yy < ${kernelSize}; yy ++) {
            for (int xx = 0; xx < ${kernelSize}; xx ++) {
                vec2 samplePos = pos + vec2(yy - offset, xx - offset) * onePixel;
                vec4 color = texture2D(u_texture, samplePos);
                if (color.a == 1.0) {
                    int red = int(color.r * 255.0);
                    for (int i = 0; i<256; i++) {
                        if (i == red) {
                            histogram[i]++;
                            maxValue = int(max(float(maxValue), float(histogram[i])));
                        }
                    }
                }
            }
        }

        /* the number of values that have the maximum mode value */
        int numModes = 0;

        /* the colour (index) of the maximum node value */
        int modeValue = 0;

        for (int i = 0; i<256; i++) {
            if (histogram[i] == maxValue) {
                numModes++;
                modeValue = i;
            }
        }

        if (numModes == 1) {
            outColor.r = float(modeValue) / 255.0;
        }

        outColor.g = outColor.r;
        outColor.b = outColor.r;

        gl_FragColor = outColor;
    }

标签: glslwebglshader

解决方案


这实际上是一个非常有趣的问题。

使这些类型的事情更快的通常方法是使用多次传递。您将首先应用 3x1 内核,然后将 1x3 内核应用于结果,而不是调用 3x3 内核。

然而,模式过滤器是不可分离的。我刚刚想出了一个会崩溃的案例:

151
555
151

中间像素应该变成 5,但如果你把它分解成一个 h+v 传递,结果将是 1。

话虽如此,我仍然认为多通道解决方案会有所帮助,如果你能想出一个。例如,您可以将模式值写入红色通道并将其计数写入绿色通道。这将允许您组合多个小内核通道以有效地实现大内核大小,尽管我认为它会偶尔出现错误,类似于我上面的示例。为了对抗错误,您可以将“第二大”模式值/计数写入 BLUE 和 ALPHA。


推荐阅读