我是OpenGL的新手,我很期待比较两种纹理,以了解它们彼此之间的相似程度。我知道如何使用两个位图图像来实现此目的,但我确实需要使用一种方法来比较两个纹理。
问题是:当我们比较两个图像时,有什么方法可以比较两个纹理吗?像一个像素一个像素地比较两个图像?
实际上,您似乎要求的是不可能的,或者至少不像在GPU上那样容易。问题在于,GPU被设计为在最短的时间内完成尽可能多的小任务。不包括对数据数组(例如像素)进行迭代,因此获取诸如整数或浮点值之类的内容可能会有些困难。
您可以尝试执行一种非常有趣的过程,但是我不能说结果适合您:
您可以先创建一个新的纹理,该纹理是两个输入纹理之间的差异,然后继续对结果进行下采样直到1x1像素纹理,然后获取该像素的值以查看其差异。
为此,最好使用目标缓冲区的固定大小,该大小为POT(2的幂),例如256x256。如果您未使用固定尺寸,则结果可能会因图像尺寸而有很大差异。
因此,在第一遍中,您需要将两个纹理重绘到第三个纹理(使用FBO-帧缓冲区对象)。您将使用的着色器很简单:
vec4 a = texture2D(iChannel0,uv); vec4 b = texture2D(iChannel1,uv); fragColor = abs(a-b);
因此,现在您有了一个纹理,代表每个像素,每个颜色分量的两个图像之间的差异。如果两个图像相同,则结果将是全黑图像。
现在,您将需要创建一个新的FBO,该FBO在每个维度上都按比例缩放一半,在本示例中为128x128。要绘制到此缓冲区,您需要将其GL_NEAREST
用作纹理参数,这样就不会在纹理像素获取上进行任何插值。然后为每个新像素求和源图像中最近的4个像素:
vec4 originalTextCoord = varyingTextCoord; vec4 textCoordRight = vec2(varyingTextCoord.x+1.0/256, varyingTextCoord.y); vec4 textCoordBottom = vec2(varyingTextCoord.x, varyingTextCoord.y+1.0/256); vec4 textCoordBottomRight = vec2(varyingTextCoord.x+1.0/256, varyingTextCoord.y+1.0/256); fragColor = texture2D(iChannel0, originalTextCoord) + texture2D(iChannel0, textCoordRight) + texture2D(iChannel0, textCoordBottom) + texture2D(iChannel0, textCoordBottomRight);
256值来自源纹理,因此应统一显示,以便您可以重复使用相同的着色器。
绘制后,您需要下拉至64、32、16 ...,然后将像素读回CPU并查看结果。
现在很遗憾,此过程可能会产生非常不希望的结果。由于将颜色简单地加在一起,这将对所有不够相似的图像产生溢出(导致白色像素,或者导致(1,1,1,0)
不透明)。首先可以通过在第一个着色器通道上使用缩放比例来将输出除以足够大的值来克服。但这可能还不够,可能需要在第二个着色器中进行平均(将所有texture2D
调用乘以.25
)。
最后,结果可能仍然有些奇怪。在CPU上有4种颜色分量,分别代表图像差异的和或平均值。我想您可以对它们进行总结,然后选择要考虑的图像,以使图像彼此相似。但是,如果您希望对结果有更多了解,则可能需要将整个像素视为单个32位浮点值(这有些棘手,但您可能会在SO周围找到答案)。这样,您可以计算出没有溢出的值,并从算法中获得相当准确的结果。这意味着您将写浮动值,就好像它是从第一个着色器输出开始并继续进行其他每一次绘制调用的颜色一样(获取纹理像素,将其转换为浮点,求和,将其转换回vec4并分配为输出) ,GL_NEAREST
在这里至关重要。
如果没有,那么您可以优化过程并使用GL_LINEAR
代替它,GL_NEAREST
而只是继续重画差异纹理,直到它达到单个像素大小(不需要4个坐标)。这将产生一个不错的像素,该像素代表差异纹理中所有像素的平均值。因此,这是两个图像中像素之间的平均差异。同样,此过程应该非常快。
然后,如果您想执行更智能的算法,则可能会在创建差异纹理方面做一些奇迹。简单地减去颜色可能不是最好的方法。模糊其中一个图像,然后将其与另一个图像进行比较会更有意义。对于那些非常相似的图像,这将失去精度,但是对于其他所有图像,它将给您带来更好的结果。举例来说,您可以说,仅当像素与另一张图像(模糊的图像)的权重相差30%时,您才感兴趣,因此您将舍弃并缩放每个分量的30%,例如result.r = clamp(abs(a.r-b.r)-30.0/100.0, .0, 1.0)/((100.0-30.0)/100.0);