ImageMagick can do it, although it’s a bit convoluted. One way is:
convert img1 img2 -fx "(((255*u)&(255*(1-v)))|((255*(1-u))&(255*v)))/255" img_out
(img1,img2,img_out are the two input and single output file names respectively).
Explanation
It’s a bit ugly (I’m sure someone with more ImageMagick-fu than me could clean it up but it works like this:
-
-fx "xxx"basically says “perform the operationxxxon the image”.
In the expression above,uandvstand for the first and second input images respectively. -
Now,
-fxonly has bitwise AND&and bitwise OR|in the way of bitwise operators.
To reconstruct bitwise XOR, we needconvert img1 img2 -fx "(u & NOT v) | (NOT u & v)" img_out -
To get the
NOT(there is a logicalNOTbut no bitwiseNOT), we remember thatNOT x = 255-xifxis 8-bit.
So to getNOT uwe can just do255-u, assuming imageuis 8-bit.
Hence, the ImageMagick command would be:convert img1.png img2.img -fx "((255-u)&v)|(u&(255-v))" image_xor.png -
-
The one problem here is that when ImageMagick does
fxit normalises all the pixels inuandvin the range[0,1]instead of[0,255]as we expect, and doing bitwise on non-integers screws stuff up. -
Hence, we have to multiply all occurrences of
uandvin the above expression by 255 (so the bitwise operations work), and divide by 255 at the very end to get back in the range[0,1]that ImageMagick expects.
-
This gives us the original command,
convert img1 img2 -fx "(((255*u)&(255*(1-v)))|((255*(1-u))&(255*v)))/255" img_out
Voila!