Spaces:
Sleeping
Sleeping
""" | |
Program to compare the similarity of a 300x300 input and mosaic image using three metrics: | |
MSE: Pixel-level accuracy. | |
SSIM: Perceptual similarity. | |
Histogram Similarity: Color distribution match. | |
Author: Peter Chibuikem Idoko | |
""" | |
import cv2 | |
import os | |
import numpy as np | |
import logging | |
import csv | |
from skimage.metrics import structural_similarity as ssim | |
# Configure logging | |
logging.basicConfig( | |
filename="similarity.log", | |
level=logging.INFO, | |
format="%(asctime)s - %(levelname)s - %(message)s", | |
) | |
# Constants | |
INPUT_IMAGE = "scraped_photos/image_18.jpg" # Path to the original scraped image | |
MOSAIC_IMAGE = "output/reconstructed_image.jpg" # Path to the final mosaic image | |
IMAGE_SIZE = 300 # Images are resized to 300x300 | |
def compute_mse(imageA, imageB): | |
""" | |
Computes the Mean Squared Error (MSE) between two images. | |
Lower MSE means the images are more similar. | |
Args: | |
imageA (numpy.ndarray): First image. | |
imageB (numpy.ndarray): Second image. | |
Returns: | |
float: Mean Squared Error value. | |
""" | |
err = np.mean((imageA.astype("float") - imageB.astype("float")) ** 2) | |
return err | |
def compute_ssim(imageA, imageB): | |
""" | |
Computes the Structural Similarity Index (SSIM) between two images. | |
SSIM ranges from -1 to 1, where 1 means identical images. | |
Args: | |
imageA (numpy.ndarray): First image. | |
imageB (numpy.ndarray): Second image. | |
Returns: | |
float: SSIM score. | |
""" | |
ssim_scores = [ | |
ssim(imageA[:, :, i], imageB[:, :, i]) for i in range(3) | |
] | |
return np.mean(ssim_scores) # Average SSIM over RGB channels | |
def compute_histogram_similarity(imageA, imageB): | |
"""Compares color histograms using Chi-Square (more sensitive).""" | |
histA = cv2.calcHist([imageA], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256]) | |
histB = cv2.calcHist([imageB], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256]) | |
histA = cv2.normalize(histA, histA).flatten() | |
histB = cv2.normalize(histB, histB).flatten() | |
return cv2.compareHist(histA, histB, cv2.HISTCMP_CHISQR) | |
def measure_similarity(image1_path, image2_path): | |
""" | |
Measures the similarity between two images using MSE, SSIM, and histogram comparison. | |
Args: | |
image1_path (str): Path to the first image (original quantized image). | |
image2_path (str): Path to the second image (mosaic image). | |
Returns: | |
dict: A dictionary containing similarity scores. | |
""" | |
# Load images | |
image1 = cv2.imread(image1_path) | |
image2 = cv2.imread(image2_path) | |
if image1 is None or image2 is None: | |
print("Error: Could not load one or both images.") | |
return None | |
# Resize images to ensure they are the same size | |
image1 = cv2.resize(image1, (IMAGE_SIZE, IMAGE_SIZE)) | |
image2 = cv2.resize(image2, (IMAGE_SIZE, IMAGE_SIZE)) | |
# Compute similarity metrics | |
mse_value = compute_mse(image1, image2) | |
ssim_value = compute_ssim(image1, image2) | |
hist_sim_value = compute_histogram_similarity(image1, image2) | |
# Generate log message | |
log_message = ( | |
f"\nImage Similarity Analysis\n" | |
f"Mean Squared Error (MSE): {mse_value:.2f} (Lower is better)\n" | |
f"Structural Similarity Index (SSIM): {ssim_value:.3f} (Closer to 1 is better)\n" | |
f"Histogram Similarity: {hist_sim_value:.3f} (Closer to 1 is better)\n" | |
) | |
# Print and log the results | |
print(log_message) | |
logging.info(log_message) | |
# Return results as a dictionary | |
return { | |
"MSE": mse_value, | |
"SSIM": ssim_value, | |
"Histogram Similarity": hist_sim_value | |
} | |
def log_results_to_csv(results: dict, output_csv: str = "similarity_results.csv") -> None: | |
""" | |
Logs the computed similarity metrics to a CSV file. | |
Args: | |
results (dict): A dictionary containing similarity metrics (MSE, SSIM, Histogram Similarity). | |
output_csv (str): Path to the CSV file where results should be saved. Default is "similarity_results.csv". | |
Returns: | |
None | |
Edge Cases: | |
- If the results dictionary is empty or None, the function does nothing. | |
- If the CSV file does not exist, it creates one with headers. | |
- If an I/O error occurs, it logs the issue and prevents data loss. | |
""" | |
if not results: | |
logging.warning("No results to log. Skipping CSV logging.") | |
return | |
file_exists = os.path.isfile(output_csv) | |
try: | |
with open(output_csv, mode="a", newline="") as csv_file: | |
fieldnames = ["MSE", "SSIM", "Histogram Similarity"] | |
writer = csv.DictWriter(csv_file, fieldnames=fieldnames) | |
# Write header only if the file is newly created | |
if not file_exists: | |
writer.writeheader() | |
writer.writerow(results) | |
logging.info(f"Similarity results logged to {output_csv}") | |
except IOError as e: | |
logging.error(f"Error writing to CSV file {output_csv}: {e}") | |
if __name__ == "__main__": | |
output = measure_similarity(INPUT_IMAGE, MOSAIC_IMAGE) | |
log_results_to_csv(output) | |