Image.blend() tends to lose half a unit of
brightness (on a scale of 0-255) wherever the two input images differ.
I noticed this while blending a series of greyscale book page images (black
background, white text) in a loop, blending the n-th page in to the composite of the
previous ones with alpha=1/n – i.e. first page is taken at full value, then
second blended at alpha=1/2, third blended to that result with alpha = 1/3 etc.But this inevitably results in an all-black image. But it should result in a final image where all pages have equal weight.
Turns out that there's a roundoff problem in Image.blend(im1,im2,alpha) which calculates(UINT8)(im1 + alpha*(im2-im1)) at each pixel,
but since UINT8 acts like floor() the result of the calculation is always rounded
down.The right fix is to add half a unit
before the floor, i.e. instead calculate (uint8) (im1 + alpha*(im2-im1)+0.5).This has no effect on points where the images
match (the 0.5 just gets discarded again), but removes the downward bias when they don’t.
You can work around this using a combination of
ImageChops.add() and subtract(), remembering that subtract truncates negative
values at 0:
def blend_unbiased (im1,im2,alpha):
# because subtract()
truncates at zero, we have to use two steps to include both the positive and