mosaicGenerator / compare.py
Peter Idoko
Initial fresh commit
b8bad50
"""
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)