I’m sure you’ve seen the image view modes Github released last month. It’s a really nice way to see the differences between two versions of an image. In this article, I’ll try to explain how an image diff could be built using pure Ruby and ChunkyPNG.

If you need a more basic introduction to working with pixel data in ChunkyPNG, check out last week’s article, which I did some blob detection.

Finding differences in images works by looping over each pixel in the first image and checking if it’s the same as the pixel in the same spot in the second image. An implementation might look like this:

require 'chunky_png' images = [ ChunkyPNG::Image.from_file('1.png'), ChunkyPNG::Image.from_file('2.png') ] diff = [] images.first.height.times do |y| images.first.row(y).each_with_index do |pixel, x| diff << [x,y] unless pixel == images.last[x,y] end end puts "pixels (total): #{images.first.pixels.length}" puts "pixels changed: #{diff.length}" puts "pixels changed (%): #{(diff.length.to_f / images.first.pixels.length) * 100}%" x, y = diff.map{ |xy| xy[0] }, diff.map{ |xy| xy[1] } images.last.rect(x.min, y.min, x.max, y.max, ChunkyPNG::Color.rgb(0,255,0)) images.last.save('diff.png')

Want the code? Here’s a Gist.

After loading in the two images, we’ll loop over the pixels of the first one. If the pixel is the same as the one in the second image, we’ll add it to the diff array. When we’re done, we’ll draw a bounding box around the area that contains the changes:

It worked! The result image has a bounding box around the hat we added to the image and the output tells us that almost 9% of the pixels in the image changed, which seems about right.

pixels (total): 16900 pixels changed: 1502 pixels changed (%): 8.887573964497042%

A problem with this approach is that it only detects change, without measuring it. It doesn’t care if the pixel it’s looking at is just a bit darker or a completely different color. If we use this code to compare one image to a slightly darker version of itself, the result will look like this:

pixels (total): 16900 pixels changed: 16900 pixels changed (%): 100.0%

This would mean that the two images are completely different, while (from a human eye’s perspective) they’re almost the same. To get a more accurate result, we’ll need to measure the difference in the pixels’ colors.