I'd been doing some research into calculating similarity scores between images, and came across a great article by Adrian Rosebrock showing one way you can do this in Python using the Structural Similarity Index (SSIM) (an implementation for this can handily be found in the scikit-image package).
What he was describing looked to me like a great way to solve a 'Spot The Difference' puzzle - so we'll use SSIM to do just that. I'm making use of Adrian's code below, so full credit goes to him.
I've created what I believe to be a very difficult puzzle for humans to solve. Image B has only two blocks that are different colours from Image A, the rest are exactly the same
Perhaps you've got a sharp eye and can spot them without the use of Computer Vision. Let's do the latter anyway...
1. Import the required packages:
from skimage.measure import compare_ssim
2. Read in our two images:
imageA = cv2.imread("spot_the_diff1.png")
imageB = cv2.imread("spot_the_diff2.png")
3. Convert the images to grayscale (this doesn't actually matter to us, but we'll follow Adrian's code anyway)
grayA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY)
grayB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY)
4. According to SSIM, how similar are the images (1 being identical, -1 being completely different)
(score, diff) = compare_ssim(grayA, grayB, full=True)
diff = (diff * 255).astype("uint8")
This score would suggest that the two pictures are extremely similar!
You'll notice above, we not only used the compare_ssim function to obtain a score, but also an object called diff. This diff object represents the actual differences in the image. To carry on processing this object using OpenCV we multiply the values by 255 and change it to uint8 format
5. Threshold the diff image, and find contours which will showcase the regions in the images that are different
thresh = cv2.threshold(diff, 0, 255,cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
6. Loop through the contours and create bounding boxes on our two images
for c in cnts:
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(imageA, (x, y), (x + w, y + h), (0, 0, 255), 2)
cv2.rectangle(imageB, (x, y), (x + w, y + h), (0, 0, 255), 2)
7. Show our images - and uncover the differences!
I hope you found this interesting and useful! As mentioned above, credit for the original code goes to Adrian Rosebrock and pyimagesearch.com. Head there for a whole raft of amazing Python tips and projects.