Search code examples
imagepython-imaging-library

Compare two images and highlight differences along on the second image


Below is the current working code in python using PIL for highlighting the difference between the two images. But rest of the images is blacken.

Currently i want to show the background as well along with the highlighted image.

Is there anyway i can keep the show the background lighter and just highlight the differences.

from PIL import Image, ImageChops
point_table = ([0] + ([255] * 255))

def black_or_b(a, b):
    diff = ImageChops.difference(a, b)
    diff = diff.convert('L')
    # diff = diff.point(point_table)
    h,w=diff.size
    new = diff.convert('RGB')
    new.paste(b, mask=diff)
    return new

a = Image.open('i1.png')
b = Image.open('i2.png')
c = black_or_b(a, b)
c.save('diff.png')

!https://drive.google.com/file/d/0BylgVQ7RN4ZhTUtUU1hmc1FUVlE/view?usp=sharing


Solution

  • PIL does have some handy image manipulation methods, but also a lot of shortcomings when one wants to start doing serious image processing -

    Most Python lterature will recomend you to switch to use NumPy over your pixel data, wich will give you full control - Other imaging libraries such as leptonica, gegl and vips all have Python bindings and a range of nice function for image composition/segmentation.

    In this case, the thing is to imagine how one would get to the desired output in an image manipulation program: You'd have a black (or other color) shade to place over the original image, and over this, paste the second image, but using a threshold (i.e. a pixel either is equal or is different - all intermediate values should be rounded to "different) of the differences as a mask to the second image.

    I modified your function to create such a composition -

    from PIL import Image, ImageChops, ImageDraw
    point_table = ([0] + ([255] * 255))
    
    def new_gray(size, color):
        img = Image.new('L',size)
        dr = ImageDraw.Draw(img)
        dr.rectangle((0,0) + size, color)
        return img
    
    def black_or_b(a, b, opacity=0.85):
        diff = ImageChops.difference(a, b)
        diff = diff.convert('L')
        # Hack: there is no threshold in PILL,
        # so we add the difference with itself to do
        # a poor man's thresholding of the mask: 
        #(the values for equal pixels-  0 - don't add up)
        thresholded_diff = diff
        for repeat in range(3):
            thresholded_diff  = ImageChops.add(thresholded_diff, thresholded_diff)
        h,w = size = diff.size
        mask = new_gray(size, int(255 * (opacity)))
        shade = new_gray(size, 0)
        new = a.copy()
        new.paste(shade, mask=mask)
        # To have the original image show partially
        # on the final result, simply put "diff" instead of thresholded_diff bellow
        new.paste(b, mask=thresholded_diff)
        return new
    
    
    a = Image.open('a.png')
    b = Image.open('b.png')
    c = black_or_b(a, b)
    c.save('c.png')