在图像上进行位异或操作的方法搜索

30

我希望找到一种方法在命令行(或者其他可用于程序或脚本)上获取两张图片的按位异或

这应该会得到与支持XOR混合模式的图片编辑器(Paint.NET,Photoshop等)中使用相同的最终图片。

举个例子,假设我有图像A:

Image A(Seattle, from the paint.net documentation

图像B:

ImageB(Apple, from the paint.net documentation)

如果一切顺利,最终结果应该是这样的:

Image C(Xor result of above images, from the paint.net documentation)

这个有趣的部分当然是,当你再次对图像C进行异或运算时,你将得到图像A的精确副本。
现在,我已经在互联网上搜索了很久,想找到一种以编程方式实现这个过程的方法,但我什么也没找到。即使是ImageMagick也不支持对图像进行按位异或运算。
有人知道如何做吗?
6个回答

35

ImageMagick可以实现,虽然有点复杂。其中一种方法是:

convert img1 img2 -fx "(((255*u)&(255*(1-v)))|((255*(1-u))&(255*v)))/255" img_out

(img1, img2, img_out 分别是两个输入文件和一个输出文件的文件名。)

说明

这段代码有点混乱(我相信某些比我更懂ImageMagick的人可以整理得更好,但它就是这样运作的:

  1. -fx "xxx"基本上表示“对图像执行操作xxx”。 在上面的表达式中,uv分别代表第一个和第二个输入图像。

  2. 现在,-fx只有位与&和位或|这两种按位运算符。 要重建位异或运算,我们需要使用

    convert img1 img2 -fx "(u & NOT v) | (NOT u & v)" img_out
    
  3. 要获取NOT(有逻辑非但没有按位非),我们要记住如果x是8位,那么NOT x = 255-x。 因此,要获取 NOT u,我们只需要做 255-u,假设图像u是8位的。 因此,ImageMagick 命令应该是:

  4. convert img1.png img2.img -fx "((255-u)&v)|(u&(255-v))" image_xor.png
    
    • 问题在于当 ImageMagick 进行 fx 操作时,它将 uv 中所有像素的范围从我们所期望的 [0,255] 标准化为 [0,1] 范围,而对非整数进行位运算会导致出现问题。

    • 因此,我们需要将上述表达式中所有的 uv 的出现次数乘以 255(这样位运算才能正常工作),并在最后除以 255,以恢复到 ImageMagick 所期望的 [0,1] 范围。

这给了我们原始命令,

convert img1 img2 -fx "(((255*u)&(255*(1-v)))|((255*(1-u))&(255*v)))/255" img_out

这就是了!


2
看起来很棒 ;D。不过你的代码有一个小错误(第二个u应该是v)。我已经修复了它。但运行时间确实太长了... ^^' - Qqwy
它很遗憾需要运行这么长时间(我只在你那里的那些小图像上尝试过)。编写一些代码仍然是最快的方法,“-fx”在imagemagick中的声誉是较慢的。 - mathematical.coffee
ImageMagick-7.0.11-Q8>magick.exe 1.png 2.png -fx "u&(1-v)|(1-u)&v" img.png(已测试1位和8位图像) - qqqq1961

27

我发现需要在图像上使用xor,而G'MIC工具对我有用。 G'MIC非常强大,正如Image Magick一样,但是值得一试,可以解决一些棘手的图像处理问题。

gmic a.png b.png -blend xor -o result.png

G'MIC也可以直接处理上面发布的图像。

gmic http://i.stack.imgur.com/Ws6e8.png http://i.stack.imgur.com/hoBIM.png -blend xor -o result.png

需要帮助,

gmic -h -blend

输入图像描述


2
非常遗憾只能选择一个答案,因为这个gmic看起来是一个非常棒的工具。(我选择了原本使用的答案,虽然两年前我应该也选择了,但当时忘记了)。感谢您提供的这个优秀的答案! - Qqwy

7

以下是我在Java中的做法:

同时迭代两个图像的所有像素(在for循环(y)内部使用for循环(x))。当然,要使用 BufferedImage 。您可以通过以下方法获取像素的颜色:

int color = img.getRGB(x, y);

同样的方法也要用在另一张图片上,并将两种颜色进行异或操作,将结果值存储在一个新的BufferedImage中,其尺寸与两个输入图像相同。

以下是一些示例代码:

public static BufferedImage xorEffect(BufferedImage imageA, BufferedImage imageB) {
    if (imageA.getWidth() != imageB.getWidth() ||
        imageA.getHeight() != imageB.getHeight())
    {
        throw new IllegalArgumentException("Dimensions are not the same!");
    }
    BufferedImage img = new BufferedImage(imageA.getWidth(),
                                          imageA.getHeight(),
                                          BufferedImage.TYPE_INT_ARGB_PRE);

    for (int y = 0; y < imageA.getHeight(); ++y) {
        for (int x = 0; x < imageA.getWidth(); ++x) {
           int pixelA = imageA.getRGB(x, y);
           int pixelB = imageB.getRGB(x, y);
           int pixelXOR = pixelA ^ pixelB;
           img.setRGB(x, y, pixelXOR);
        }
    }
    return img;
}

要从文件加载图像,请使用以下代码:

BufferedImage imageA = ImageIO.read(new File("/home/username/image.png"));

你可能想要使用BufferedImage.TYPE_INT_RGB,这样alpha通道就不会引起差异。 - Alex Collins
1
我认为应该这样写:int pixelXOR = pixelA ^ pixelB ^ 0xFF000000; - Vladimir Zolotov

3

如果你想要自己做,就要一像素一像素地完成。如果你需要一个库,我推荐 OpenCV。这是一个非常好的开源库,在图像处理领域支持大量运算。它支持使用 ^ 运算符进行直接的 XOR 运算。祝好运。


1

知道

A XOR B = (A AND NOT B) OR (NOT A AND B)。

而且,大多数常见的图像处理工具都具有and、or和not操作,因此其余部分非常容易 :)

在Python中工作,您可以编写一个简单的脚本执行该操作,甚至将其添加为gimp的插件 ;)


1
顺便说一句,我可能有一个使用Python和OpenCV快速且(某种程度上)优雅的实现。如果需要的话,我可以在这里发送它 ;) - jlengrand

1

我只想对一张图像进行异或操作,而不是与另一张图像进行操作,而是与一个二进制密钥进行操作。我看到OpenCV的方法需要两张图像...我该如何做到这一点呢?非常感谢 编辑:好吧,我看到了https://gmic.eu/reference/xor.html。我会尝试一下。 - undefined

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