在Java中计算两个ARGB整数之间差异的最快方法是什么?

3

给定一个DataBuffer中的int值,其中包含使用以下掩码打包的ARGB数据

A = 0xFF000000 R = 0xFF0000 G = 0xFF00 B = 0xFF

我正在执行以下操作,但不知道Java中是否有更快的方法?

        DataBuffer db1 = img1.getData().getDataBuffer();
        DataBuffer db2 = img2.getData().getDataBuffer();

        int x, y;
        int totalDiff = 0;
        for (int i = 0; i < WIDTH * HEIGHT; ++i) {
            x = db1.getElem(i);
            y = db2.getElem(i);

            totalDiff += Math.abs((x & 0xFF) - (y & 0xFF))
                       + Math.abs(((x & 0xFF00) >> 8) - ((y & 0xFF00) >> 8))
                       + Math.abs(((x & 0xFF0000) >> 16) - ((y & 0xFF0000) >> 16 ));
        }

1
0xFF0000000x00FF0000 之间应该有什么区别? - Joachim Sauer
没有区别,上面的代码忽略了A,从x和y中提取了单独的R、G和B分量,并得到它们的差值,然后加上它们差值的绝对值。所以如果x是0xFF010101,y是0xFF020202,那么它们的差值就是3。 - Johnny
0xFF010104和0xFF020202之间的差异是0吗?只是想确认这是否是有意的。 - Joachim Sauer
是的,没错。这只是我使用的一种启发式方法,但它确实成为了瓶颈。我怀疑用一些技巧来替换对Math.abs的调用可能会有所改善。转储字节码显示它已经相当短了。 - Johnny
也许直接将abs(int)的代码放入函数中,但我认为Hotspot会为我优化它。 - Johnny
显示剩余5条评论
3个回答

3

如果你真的需要加速,你可能需要检查 DataBuffer 的类型并为具体的类型提供优化代码,这样你就可以节省对 getElem(i) 的调用。这会稍微加速你的代码。

类似于这样:

    DataBuffer db1 = img1.getData().getDataBuffer();
    DataBuffer db2 = img2.getData().getDataBuffer();

    int totalDiff = 0;
    int x, y;
    if (db1 instanceof DataBufferInt && db2 instanceof DataBufferInt) {
        int[] data1 = ((DataBufferInt) db1).getData();
        int[] data2 = ((DataBufferInt) db2).getData();
        for (int i = 0; i < WIDTH * HEIGHT; ++i) {
            x = data1[i];
            y = data2[i];

            totalDiff += Math.abs((x & 0xFF) - (y & 0xFF))
                + Math.abs(((x & 0xFF00) >> 8) - ((y & 0xFF00) >> 8))
                + Math.abs(((x & 0xFF0000) >> 16) - ((y & 0xFF0000) >> 16));
        }
    } else {
        for (int i = 0; i < WIDTH * HEIGHT; ++i) {
            x = db1.getElem(i);
            y = db2.getElem(i);

            totalDiff += Math.abs((x & 0xFF) - (y & 0xFF))
                    + Math.abs(((x & 0xFF00) >> 8) - ((y & 0xFF00) >> 8))
                    + Math.abs(((x & 0xFF0000) >> 16) - ((y & 0xFF0000) >> 16));
        }
    }

编辑: 另一个想法可以大大提高速度。如果这只是一种启发式方法,那么计算图像的某种“下采样”版本的差异可能就足够了。将 ++i 替换为 i+=10,可以加快速度达到10倍。当然,如果这有意义取决于您的图像类型。

编辑: 在一条评论中,您提到这是 GA 的适应函数...在这种情况下,从您的图像中获取 100 (或仅10个?) 随机位置,并比较该位置的像素就足够了。获得的速度提升很可能会超过精度损失。


这并不会得到相同的结果:r、g、b 组件没有分别进行比较。 - Maurice Perry
嘿;)。好的...所以我们可以使用这种方法仅保存对“getElem(i)”的调用...这只会提高约10%的速度...总比没有好。 - Arne Deutsch
我已经尝试过采样,但它并不起作用。有时变化非常小,需要比较所有像素以获得最佳性能。 - Johnny

1
同意@Arne。
你还可以去掉右移操作。
(x & 0xFF0000) >> 16) - ((y & 0xFF0000) >> 16 ). 

你知道 abs(XX0000 - YY0000) 只会在 0-255 的范围内。

如果你能提出你想要确定的内容,那会很有帮助。

也就是说,像色度(YUV、YCrCb)这样的方式是否更有利于你想要实现的目标来存储像素信息?


1
abs(XX0000 - YY0000) 在范围 [-FF0000,FF0000] 内。你仍需要进行右移,但可以在 abs 操作之后执行:abs((x & 0xFF0000) - (y & 0xFF0000)) >> 16 - Jordi
很好,这只是一个遗传算法用来将两幅图像计算成一个数字的启发式方法。0表示没有差异。 - Johnny
在这种情况下,如果totalDiff> 0,您可以“break”退出循环。 - Adrian Regan
很遗憾,不行。有N张图片需要排名。如果你只是抽样,可能会错误地对它们进行排名。 - Johnny

0
如果计算平方和对您来说是可以接受的,那么速度会更快:
    for (int i = 0; i < WIDTH * HEIGHT; ++i) {
        x = db1.getElem(i);
        y = db2.getElem(i);
        int dr = ((x & 0xFF0000) >> 16) - ((y & 0xFF0000) >> 16 );
        int dg = ((x & 0xFF00) >> 8) - ((y & 0xFF00) >> 8);
        int db = (x & 0xFF) - (y & 0xFF);
        totalDiff += dr*dr + dg*dg + db*db;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接