Skip to content

Instantly share code, notes, and snippets.

@santaklouse
Created August 14, 2023 06:04
Show Gist options
  • Save santaklouse/8278f47d79db1a4634764a1932bb88d7 to your computer and use it in GitHub Desktop.
Save santaklouse/8278f47d79db1a4634764a1932bb88d7 to your computer and use it in GitHub Desktop.
Calculate two images and calculate pixel difference between images and SSIM (Similarity) Score in percents.
#!/usr/bin/env python3
# Compares two images and calculate pixel difference between images and SSIM (Similarity) Score in percents.
# Images should have same dimentions,
# BUT
# if dimentions of the second image is bigger than first, script will try to find first image on second by template and crop second.
import sys
import os.path
def help():
print('Usage: ', sys.argv[1], ' <baseImage> <secondImage>')
img1_filename=sys.argv[1]
img2_filename=sys.argv[2]
for x in [img1_filename, img2_filename]:
if os.path.exists(x) == False:
print(x, 'not exists')
help()
exit(1)
print('loading libraries: ', end="", flush=True)
import filetype
print('filetype', end=" ", flush=True)
import cv2
print('cv2', end=" ", flush=True)
import numpy
print('numpy', end=" ", flush=True)
import matplotlib.pyplot as plt
print('matplotlib.pyplot', end=" ", flush=True)
from torch import from_numpy
print('torch', end=" ", flush=True)
from skimage.metrics import structural_similarity as ssim
print('skimage.metrics.structural_similarity', flush=True)
for x in [img1_filename, img2_filename]:
if filetype.is_image(x) == False:
print(x, 'not image')
help()
exit(1)
def center_crop(img, new_width=None, new_height=None):
print('center_crop')
width = img.shape[1]
height = img.shape[0]
if new_width is None:
new_width = min(width, height)
if new_height is None:
new_height = min(width, height)
left = int(numpy.ceil((width - new_width) / 2))
right = width - int(numpy.floor((width - new_width) / 2))
top = int(numpy.ceil((height - new_height) / 2))
bottom = height - int(numpy.floor((height - new_height) / 2))
if len(img.shape) == 2:
center_cropped_img = img[top:bottom, left:right]
else:
center_cropped_img = img[top:bottom, left:right, ...]
return center_cropped_img
def tensorify(img):
# convert image to tensor format and normalize pixel values between 0 and 1
# img_tensor = torch.from_numpy(img.transpose((2, 0, 1)))# / 255
img_tensor = from_numpy(img.transpose((1, 2, 0))) #/ 255
# torch.Tensor(i)
return img_tensor
def calc_perc(i_x, t_x):
return (t_x * 100 / i_x - 100) * -1
def detect_by_template_and_crop(template, image):
img_rgb = image
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
# template
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
w, h = template.shape
image_w, image_h, a = image.shape
print('template: ', w, h)
print('image: ', image_w, image_h)
if w > image_w or h > image_h:
p_w = 0
p_h = 0
p = 100
if w>image_w:
p_w = calc_perc(w, image_w)
if h>image_h:
p_h = calc_perc(h, image_h)
if p_w > p_h:
p = 100 - p_w
if p_h > p_w:
p = 100 - p_h
if p != 100:
p = int(p - 1)
print('percent of original template size:', p)
width = int(template.shape[1] * p / 100)
height = int(template.shape[0] * p / 100)
dim = (width, height)
# resize image
template = cv2.resize(template, dim, interpolation = cv2.INTER_AREA)
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
_, _, _, maxLoc=cv2.minMaxLoc(res)
cv2.rectangle(img_rgb, maxLoc, (maxLoc[0]+h, maxLoc[1]+w), (0, 255, 255), 2)
# # show images for debug
# cv2.imshow('Detected', img_rgb)
crop_img = img_rgb[maxLoc[1]:maxLoc[1]+w, maxLoc[0]:maxLoc[0]+h, :]
print('detected and cropped:', 'original size:', image.shape, 'new size:', crop_img.shape)
# # show images for debug
# cv2.imshow("cropped", crop_img)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
# exit()
return crop_img
def crop(img, w, h):
print("cropping img")
return center_crop(img2, min_width, min_height)
def calculate_absdiff(img1, img2):
#--- take the absolute difference of the images ---
res = cv2.absdiff(img1, img2)
#--- convert the result to integer type ---
res = res.astype(numpy.uint8)
#--- find percentage difference based on number of pixels that are not zero ---
return int((numpy.count_nonzero(res) * 100)/ res.size)
def calculate_ssim(img1, img2):
_img1 = tensorify(img1) # helper function to convert cv2 image to tensors
_img2 = tensorify(img2)
# Convert PyTorch tensors to numpy arrays
img1_np = _img1.numpy()
img2_np = _img2.numpy()
# Compute SSIM score with win_size=3
ssim_score = ssim(img1_np, img2_np, multichannel=True, win_size=3)
return numpy.round(ssim_score*100, 2)
for x in [img1_filename, img2_filename]:
if os.path.exists(x) == False or filetype.is_image(x) == False:
print('file', x, 'not found or it not image')
help()
exit(1)
print("img1: ", os.path.abspath(img1_filename))
print("img2: ", os.path.abspath(img2_filename))
img1 = cv2.imread(img1_filename, cv2.COLOR_RGBA2RGB)
img2 = cv2.imread(img2_filename, cv2.COLOR_RGBA2RGB)
height1, width1 = img1.shape[0],img1.shape[1]
print("img1 w/h", width1, height1)
height2, width2 = img2.shape[0],img2.shape[1]
print("img2 w/h", width2, height2)
min_height = min(height1, height2)
min_width = min(width1, width2)
if width2 > min_width or height2 > min_height:
print("img2 bigger than img1, detecting img1 on img2 and cropping img2")
# detect img1 on img2 and crop
img2=detect_by_template_and_crop(img1, img2)
else:
height1, width1 = img1.shape[0],img1.shape[1]
height2, width2 = img2.shape[0],img2.shape[1]
min_height = min(height1, height2)
min_width = min(width1, width2)
if height1 > min_height:
img1 = crop(img1, min_width, min_height)
if height2 > min_height:
img2 = crop(img2, min_width, min_height)
height1, width1 = img1.shape[0],img1.shape[1]
height2, width2 = img2.shape[0],img2.shape[1]
if width1 > min_width:
img1 = crop(img1, min_width, min_height)
if width2 > min_width:
img2 = crop(img2, min_width, min_height)
percentage=calculate_absdiff(img1, img2)
print(f"percentage of difference by pixel: : {percentage}%")
print("calculating SSIM...")
ssim_score=calculate_ssim(img1, img2)
print("Image SSIM (Similarity) Score: ", ssim_score ,"%")
### js part, download design screenshot
# # in Figma:
# function downloadBlob(blob, name = 'file.txt') {
# // Convert your blob into a Blob URL (a special url that points to an object in the browser's memory)
# const blobUrl = URL.createObjectURL(blob);
#
# // Create a link element
# const link = document.createElement("a");
#
# // Set link's href to point to the Blob URL
# link.href = blobUrl;
# link.download = name;
#
# // Append link to the body
# document.body.appendChild(link);
#
# // Dispatch click event on the link
# // This is necessary as link.click() does not work on the latest firefox
# link.dispatchEvent(
# new MouseEvent('click', {
# bubbles: true,
# cancelable: true,
# view: window
# })
# );
#
# // Remove link from body
# document.body.removeChild(link);
# }
# // usage:
# content = await node2.exportAsync({
# format: 'JPG',
# constraint: { type: 'SCALE', value: 0.5 },
# });
# // Usage
# let jsonBlob = new Blob([content.buffer], {type: "image/jpg"})
# downloadBlob(jsonBlob, '223.jpg');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment