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 operationxxx
on the image”.
In the expression above,u
andv
stand for the first and second input images respectively. -
Now,
-fx
only 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 logicalNOT
but no bitwiseNOT
), we remember thatNOT x = 255-x
ifx
is 8-bit.
So to getNOT u
we can just do255-u
, assuming imageu
is 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
fx
it normalises all the pixels inu
andv
in 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
u
andv
in 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!