How do I compare SSIM between one image and many others using python?

T. Grove picture T. Grove · Feb 8, 2019 · Viewed 7.3k times · Source

Using this fantastic page: https://www.pyimagesearch.com/2014/09/15/python-compare-two-images/ I am able to find the SSIM between three images

# import the necessary packages
from skimage.measure import structural_similarity as ssim
import matplotlib.pyplot as plt
import numpy as np
import cv2

def mse(imageA, imageB):
    # the 'Mean Squared Error' between the two images is the
    # sum of the squared difference between the two images;
    # NOTE: the two images must have the same dimension
    err = np.sum((imageA.astype("float") - imageB.astype("float")) ** 2)
    err /= float(imageA.shape[0] * imageA.shape[1])

    # return the MSE, the lower the error, the more "similar"
    # the two images are
    return err

def compare_images(imageA, imageB, title):
    # compute the mean squared error and structural similarity
    # index for the images
    m = mse(imageA, imageB)
    s = ssim(imageA, imageB)

    # setup the figure
    fig = plt.figure(title)
    plt.suptitle("MSE: %.2f, SSIM: %.2f" % (m, s))

    # show first image
    ax = fig.add_subplot(1, 2, 1)
    plt.imshow(imageA, cmap = plt.cm.gray)
    plt.axis("off")

    # show the second image
    ax = fig.add_subplot(1, 2, 2)
    plt.imshow(imageB, cmap = plt.cm.gray)
    plt.axis("off")

    # show the images
    plt.show()

# load the images -- the original, the original + contrast,
# and the original + photoshop
original = cv2.imread("images/jp_gates_original.png")
contrast = cv2.imread("images/jp_gates_contrast.png")
shopped = cv2.imread("images/jp_gates_photoshopped.png")

# convert the images to grayscale
original = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY)
contrast = cv2.cvtColor(contrast, cv2.COLOR_BGR2GRAY)
shopped = cv2.cvtColor(shopped, cv2.COLOR_BGR2GRAY)

# initialize the figure
fig = plt.figure("Images")
images = ("Original", original), ("Contrast", contrast), ("Photoshopped", 
shopped)

# loop over the images
for (i, (name, image)) in enumerate(images):
    # show the image
    ax = fig.add_subplot(1, 3, i + 1)
    ax.set_title(name)
    plt.imshow(image, cmap = plt.cm.gray)
    plt.axis("off")

# show the figure
plt.show()

# compare the images
compare_images(original, original, "Original vs. Original")
compare_images(original, contrast, "Original vs. Contrast")
compare_images(original, shopped, "Original vs. Photoshopped")

However, I'm not quite sure how to apply this to many images. In particular, how could I take one image (test image) from a folder of hundreds of images and calculate MSE/SSIM between the test images and all other images?

Thank you!

Answer

Hair of Slytherin picture Hair of Slytherin · Jul 31, 2019

You simply want to loop between the different directories. This will compare to directories : first_path and second_path and all the files between them.

import os
import cv2

results = []
first_dir = os.fsencode(first_path)
second_dir = os.fsencode(second_path)

# Loop through all files in first directory
for first_file in os.listdir(first_dir):
    first_filename = os.fsdecodoe(first_file)
    first_filepath = os.path.join(os.fsdecode(first_dir), first_filename))
    if first_filename.endswith(".your_extension"):

        # Compare each file in second directory to each file in first directory
        for second_file in os.listdir(second_dir):
            second_filename = os.fsdecode(second_file)
            second_filepath = os.path.join(os.fsdecode(second_dir), second_filename)
            if second_filename.endswith(".your_extension"):
                imageA = cv2.imread(first_filepath)
                imageB = cv2.imread(second_filepath)
                (score, diff) = ssim(imageA, imageB, full=True)
                results.append((first_filepath, second_filepath, score))

Not run, but it should get you more or less get you what you need. If you want to do just one file, then take out the first loop and move the imageA = cv2.imread out to the front.