Created
December 23, 2020 12:36
-
-
Save georgescumihai/db0ea992cbce79737a9b5a899fac7c98 to your computer and use it in GitHub Desktop.
Compare two screenshots for differences and highlight the differences
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
import base64 | |
from dataclasses import dataclass | |
from typing import Any | |
import cv2 | |
import imutils | |
import numpy as np | |
from skimage.metrics import structural_similarity | |
@dataclass | |
class ImageDifference: | |
score: float | |
original: Any | |
compare: Any | |
def write_images(self, original_file_path="original", difference_file_path="difference"): | |
cv2.imwrite(str(original_file_path), self.original) | |
cv2.imwrite(str(difference_file_path), self.compare) | |
class Image(object): | |
def __init__(self, cv_image): | |
if not isinstance(cv_image, np.ndarray): | |
raise TypeError("parameter must be a cv2 image") | |
self.image = cv_image | |
@classmethod | |
def from_path(cls, file_path): | |
return cls(cv2.imread(str(file_path))) | |
@classmethod | |
def from_base64(cls, base64_image): | |
im_bytes = base64.b64decode(base64_image) | |
im_arr = np.frombuffer(im_bytes, dtype=np.uint8) # im_arr is one-dim Numpy array | |
img = cv2.imdecode(im_arr, flags=cv2.IMREAD_COLOR) | |
return cls(img) | |
class ImageCompare: | |
@staticmethod | |
def compare_images(lhs: Image, rhs: Image, threshold: int = 0.95) -> ImageDifference: | |
# https://www.pyimagesearch.com/2017/06/19/image-difference-with-opencv-and-python/ | |
# convert the images to grayscale | |
lhs_gray = cv2.cvtColor(lhs.image, cv2.COLOR_BGR2GRAY) | |
rhs_gray = cv2.cvtColor(rhs.image, cv2.COLOR_BGR2GRAY) | |
# compute the Structural Similarity Index (SSIM) between the two | |
# images, ensuring that the difference image is returned | |
(score, diff) = structural_similarity( | |
lhs_gray, rhs_gray, gaussian_weights=True, full=True, sigma=1.5, use_sample_covariance=False | |
) | |
if score >= threshold: | |
return None | |
diff = (diff * 255).astype("uint8") | |
# threshold the difference image, followed by finding contours to | |
# obtain the regions of the two input images that differ | |
thresh = cv2.threshold(diff, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1] | |
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) | |
cnts = imutils.grab_contours(cnts) | |
# loop over the contours | |
for c in cnts: | |
# compute the bounding box of the contour and then draw the | |
# bounding box on both input images to represent where the two | |
# images differ | |
(x, y, w, h) = cv2.boundingRect(c) | |
cv2.rectangle(lhs.image, (x, y), (x + w, y + h), (0, 0, 255), 2) | |
cv2.rectangle(rhs.image, (x, y), (x + w, y + h), (0, 0, 255), 2) | |
return ImageDifference(score=score, original=lhs.image, compare=rhs.image) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment