Created
August 14, 2023 06:04
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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