Image.blend() loses brightness

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

Image.blend() loses brightness

Patrick Surry-2

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 negative differences,

      # essentially calculating:

      # floor(im + max(0,(im2-im1)*alpha + 0.5) - max(0,(im1-im2)*alpha+0.5))

      im=ImageChops.add(im1,ImageChops.subtract(im2,im1,1/alpha,0.5))

      im=ImageChops.subtract(im1,ImageChops.subtract(im1,im2,1/alpha,0.5))

      return im

 

This ends up with the expected final image after a sequence of merge steps.


(This same issue likely applies to other image composition operations)


Cheers,

Patrick



_______________________________________________
Image-SIG maillist  -  [hidden email]
http://mail.python.org/mailman/listinfo/image-sig