Compare two images and highlight differences along on the second image

13,202

Solution 1

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')

Solution 2

Here's a solution using libvips:

import sys

from gi.repository import Vips

a = Vips.Image.new_from_file(sys.argv[1], access = Vips.Access.SEQUENTIAL)
b = Vips.Image.new_from_file(sys.argv[2], access = Vips.Access.SEQUENTIAL)

# a != b makes an N-band image with 0/255 for false/true ... we have to OR the
# bands together to get a 1-band mask image which is true for pixels which
# differ in any band
mask = (a != b).bandbool("or")

# now pick pixels from a or b with the mask ... dim false pixels down 
diff = mask.ifthenelse(a, b * 0.2)

diff.write_to_file(sys.argv[3])

With PNG images, most CPU time is spent in PNG read and write, so vips is only a bit faster than the PIL solution.

libvips does use a lot less memory, especially for large images. libvips is a streaming library: it can load, process and save the result all at the same time, it does not need to have the whole image loaded into memory before it can start work.

For a 10,000 x 10,000 RGB tif, libvips is about twice as fast and needs about 1/10th the memory.

Share:
13,202

Related videos on Youtube

Sumit Soman
Author by

Sumit Soman

Updated on September 16, 2022

Comments

  • Sumit Soman
    Sumit Soman over 1 year

    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

  • jsbueno
    jsbueno almost 9 years
    Oh well - the expected custom around here is to click on "accept" for the answer, rather than sending cheers! :-) Thanks anyway.
  • jsbueno
    jsbueno almost 9 years
    BTW, for one needing this for just a couple images, and don't wanting to fidle with Python installs - I answered how to the samething interactivelly using GIMP here: graphicdesign.stackexchange.com/questions/27484/…