Spaces:
Sleeping
Sleeping
File size: 5,121 Bytes
b8bad50 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
"""
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)
|