3

我正在编写一个片段着色器,以便将 9 个图像放在一起。

我以前从未使用过 GLSL,但它似乎是适合这项工作的工具,因为 OpenCL 在 iOS 上不可用,并且 CPU 上的中位数效率低下。这是我到目前为止所拥有的:

uniform sampler2D frames[9];
uniform vec2 wh;

void main(void)
{
    vec4 sortedFrameValues[9];
    float sortedGrayScaleValues[9];

    for (int i = 0; i < 9; i++)
    {
        sortedFrameValues[i] = texture2D(frames[i], -gl_FragCoord.xy / wh);
        sortedGrayScaleValues[i] = dot(sortedFrameValues[i].xyz, vec3(0.299, 0.587, 0.114));
    }

        // TODO: Sort sortedGrayScaleValues

    float gray = sortedGrayScaleValues[4];
    gl_FragColor = vec4(gray, gray, gray, 0);
}
4

4 回答 4

3

有点晚了,但我发现最快的方法是插入排序。降低着色器的复杂性和发散度是关键。双音和气泡对于小数字也很有效。一旦你起床 100 左右,切换到归并排序。

由于您知道要排序的事物数量 (9),因此您最好的选择是排序网络。您可以使用这个方便的工具来生成它...

There are 27 comparators in this network,
grouped into 11 parallel operations.

[[0,1],[2,3],[4,5],[7,8]]
[[0,2],[1,3],[6,8]]
[[1,2],[6,7],[5,8]]
[[4,7],[3,8]]
[[4,6],[5,7]]
[[5,6],[2,7]]
[[0,5],[1,6],[3,7]]
[[0,4],[1,5],[3,6]]
[[1,4],[2,5]]
[[2,4],[3,5]]
[[3,4]]

一个方便的使用方法是声明一个比较和交换宏......

#define CMP(a, b) ...
#define SWAP(a, b) ...
#define CSWAP(a, b) if (CMP(a, b)) {SWAP(a, b);}

CSWAP(0, 1); CSWAP(2, 3); ...

结合这两种方法,一个排序网络可以快速排序小块数据,然后如果你有很多块则合并排序效果很好,如复杂场景的精确 OIT 快速排序中所述(免责声明:我是作者)。展开循环(本质上是创建一个排序网络)可能特别有益,允许在寄存器中进行排序。动态索引数组放置在本地内存中,速度很慢。要强制编译器不这样做,您可以手动声明vec4 array0, array1 .... 宏可以连接文本,这在这里很有用#define CMP(a, b) (array##a < array##b)。一个相当丑陋但快速的例子是here

于 2015-03-04T14:01:30.887 回答
1

好吧,我最终实现了冒泡排序并使用了中间值。

这是我的解决方案的样子:

uniform sampler2D frames[9];
uniform vec2 wh;

vec4 frameValues[9];
float arr[9];

void bubbleSort()
{
    bool swapped = true;
    int j = 0;
    float tmp;
    for (int c = 0; c < 3; c--)
    {
        if (!swapped)
            break;
        swapped = false;
        j++;
        for (int i = 0; i < 3; i++)
        {
            if (i >= 3 - j)
                break;
            if (arr[i] > arr[i + 1])
            {
                tmp = arr[i];
                arr[i] = arr[i + 1];
                arr[i + 1] = tmp;
                swapped = true;
            }
        }
    }
}

void main(void)
{

    for (int i = 0; i < 9; i++)
    {
        frameValues[i] = texture2D(frames[i], -gl_FragCoord.xy / wh);
        arr[i] = dot(frameValues[i].xyz, vec3(0.299, 0.587, 0.114));
    }

    bubbleSort();

    float gray = arr[4];
    gl_FragColor =vec4(gray, gray, gray, 0);
}
于 2013-10-18T15:35:01.960 回答
1

您可以在您的 iOS 应用程序中使用 OpenGL ES 来查找您选择的源像素邻域半径中的中值像素值;它看起来像这样:

kernel vec4 medianUnsharpKernel(sampler u) {
vec4 pixel = unpremultiply(sample(u, samplerCoord(u)));
vec2 xy = destCoord();
int radius = 3;
int bounds = (radius - 1) / 2;
vec4 sum  = vec4(0.0);
for (int i = (0 - bounds); i <= bounds; i++)
{
    for (int j = (0 - bounds); j <= bounds; j++ )
    {
        sum += unpremultiply(sample(u, samplerTransform(u, vec2(xy + vec2(i, j)))));
    }
}
vec4 mean = vec4(sum / vec4(pow(float(radius), 2.0)));
float mean_avg = float(mean);
float comp_avg = 0.0;
vec4 comp  = vec4(0.0);
vec4 median  = mean;
for (int i = (0 - bounds); i <= bounds; i++)
{
    for (int j = (0 - bounds); j <= bounds; j++ )
    {
        comp = unpremultiply(sample(u, samplerTransform(u, vec2(xy + vec2(i, j)))));
        comp_avg = float(comp);
        median = (comp_avg < mean_avg) ? max(median, comp) : median;
    }
}

return premultiply(vec4(vec3(abs(pixel.rgb - median.rgb)), 1.0)); 
}

远没有那么复杂,不需要排序。它只涉及两个步骤: 1. 计算 3x3 邻域中源像素周围像素值的平均值;2. 找出同一邻域中小于均值的所有像素的最大像素值。3. [可选] 从源像素值中减去中间像素值进行边缘检测。

如果您使用中值进行边缘检测,有几种方法可以修改上述代码以获得更好的结果,即混合中值过滤和截断媒体过滤(替代和更好的“模式”过滤)。如果您有兴趣,请询问。

于 2015-07-04T00:17:11.470 回答
0
  • 这只是一个正常的排序问题,不是吗?我知道找到中位数的最快方法是中位数方法。

  • 在排序之前不要将您的值放入“排序”数组中可能更有意义。

  • 您不需要将sortedFrameValues变量作为数组,至少在您在这里使用它时 - 您再也不会使用任何存储的值。你只需要它作为一个变量。

于 2013-10-18T03:05:01.713 回答