1

我想要做的是,在 OpenGL 着色器中访问纹理的像素数据。之后,比较它们的红色分量,以便我可以得到具有最大红色分量的像素的坐标。我可以用 Objective C 和 CPU 处理能力来做到这一点。代码如下所示。

- (void)processNewPixelBuffer:(CVPixelBufferRef)pixelBuffer
{
    short maxR = 0;
    NSInteger x = -1, y = -1;

    CVPixelBufferLockBaseAddress(pixelBuffer, 0);    
    height = CVPixelBufferGetHeight(pixelBuffer);
    width = CVPixelBufferGetWidth(pixelBuffer);

    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
    uint8_t *src_buff = CVPixelBufferGetBaseAddress(pixelBuffer);

    short** rawData = malloc(sizeof(short*) * height);
    for (int i = 0; i < height; i++){
        rawData[i] = malloc((sizeof(short) * width));
        for (int j = 0; j < width; j++)
            rawData[i][j] = (short)src_buff[(i + width * j) * 4];
    }

    for (int j = 0; j < height; j++)
    for (int i = 0; i < width; i++)
        if (rawData[i][j] >= maxR) {
            maxR = rawData[i][j];
            x = i;
            y = j;
        }

    free(rawData);
}

所以,我的问题是,我如何使用 GPU 来完成这个过程?我可以在 OpenGL 着色器中将 pixelBuffer 作为纹理。

顶点着色器

attribute vec4 position;
attribute vec4 inputTextureCoordinate;

varying vec2 textureCoordinate;

void main()
{
    gl_Position = position;
    textureCoordinate = inputTextureCoordinate.xy;
}

片段着色器

varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture; //The input sample, in RGBA format

void main()
{
    // Code here
}

如何修改着色器以便找出具有最大红色分量的像素?然后,我想把那个像素变成红色,把其他像素变成白色。那可能吗?

4

1 回答 1

6

你可以做的是使用经典的减少着色器。您将屏幕大小的四边形渲染到输入纹理尺寸一半的纹理/屏幕(最好使用 FBO 完成),然后在片段着色器中计算每个像素的 2x2 纹素块的最大值,输出最大值和其对应的纹理坐标:

//no need for any textrue coords, we render screen-aligned anyway
uniform sampler2D inputImageTexture; //The input sample, in RGBA format
uniform vec2 invImageSize; // (1/width, 1/height)

void main()
{
    vec2 coord = (2.0 * floor(gl_FragCoord.xy) + 0.5) * invImageSize;
    float ll = texture2D(inputImageTexture, coord).r;
    float lr = texture2D(inputImageTexture, coord+vec2(invImageSize, 0.0)).r;
    float ul = texture2D(inputImageTexture, coord+vec2(0.0, invImageSize)).r;
    float ur = texture2D(inputImageTexture, coord+vec2(invImageSize, invImageSize)).r;

    vec4 color = vec4(ll, coord, 1.0);
    if(lr > color.r)
        color.xyz = vec3(lr, coord+vec2(invImageSize, 0.0));
    if(ul > color.r)
        color.xyz = vec3(ul, coord+vec2(0.0, invImageSize));
    if(ur > color.r)
        color.xyz = vec3(ur, coord+vec2(invImageSize, invImageSize));
    gl_FragColor = color;
}

然后在下一步中使用此输出纹理作为输入纹理,并再次渲染为一半大小的纹理,直到获得 1x1 纹理(或者更确切地说是可以在 CPU 上处理的小纹理)。但当然,在第二遍和所有后续遍中,您必须输出存储的纹理坐标而不是计算出的坐标。

vec2 coord = (2.0 * floor(gl_FragCoord.xy) + 0.5) * invImageSize;
vec4 ll = texture2D(inputImageTexture, coord);
vec4 lr = texture2D(inputImageTexture, coord+vec2(invImageSize, 0.0));
vec4 ul = texture2D(inputImageTexture, coord+vec2(0.0, invImageSize));
vec4 ur = texture2D(inputImageTexture, coord+vec2(invImageSize, invImageSize));

ll = (lr.r > ll.r) ? lr : ll;
ll = (ul.r > ll.r) ? ul : ll;
ll = (ur.r > ll.r) ? ur : ll;
gl_FragColor = ll;

当您最终获得具有最大红色部分的纹理坐标(作为 [0,1] 中的归一化纹理坐标)时,您只需要在此位置绘制一个完全白色的屏幕/纹理大小的四边形和一个红色点。但我不能向您保证,与纯 CPU 解决方案相比,这种多通道算法(与任务的简单性相比相当麻烦)真的会给您带来任何好处。您不能只通过一次就神奇地读取每个输出像素的所有图像像素并确定其颜色,这不是(或应该)使用片段着色器的方式。

编辑:顺便说一句。可能不需要将所有数据复制到 CPU 解决方案中的额外临时缓冲区中,至少不需要使用奇怪的数组数组将其分散在内存中,因为应该是单个内存块(或没有直接处理输入时),更不用说大容量内存分配了。因此,在考虑将任何东西转移到 GPU 上之前,首先要修复您的 CPU 解决方案。

于 2012-09-19T08:20:20.057 回答