TUTORIAL 5 – MEASURING VISUAL QUALITY OF
COMPRESSED IMAGES
Objective evaluation of visual quality deals with comparing the lossy compressed images
to their original version. The comparison is made per pixel.
For definition of quality metrics, refer to
[Link]
In this tutorial, you will learn:
• metrics on perception quality of compressed images
• how to quantitatively assess the quality of lossy compressed images against their
raw version
Technically, you are guided through:
• Loading a RAW image
• Compressing it into JPEG
• Comparing the two using MSE, PSNR, and SSIM
Step 1: Set Up the Colab Environment
Open a new Colab notebook and run the following cell to install necessary libraries:
!pip install pillow numpy scikit-image matplotlib rawpy
Step 2: Upload a RAW Image
This script lets you upload any RAW photo (.CR2, .NEF, .ARW, .DNG, etc.).
from [Link] import files
uploaded = [Link]() # Choose a RAW image file
Step 3: Convert RAW to RGB Image
We’ll use rawpy to load the RAW image and convert it into an RGB array.
import rawpy
import numpy as np
from PIL import Image
import io
# Load the uploaded RAW file
raw_filename = next(iter(uploaded))
with [Link](raw_filename) as raw:
rgb_raw = [Link]()
# Convert to PIL Image for further processing
original_img = [Link](rgb_raw)
Step 4: Save as JPEG (Simulate Compression)
# Save the RGB image as JPEG (compressed version)
jpeg_bytes = [Link]()
original_img.save(jpeg_bytes, format='JPEG', quality=50) # Lower quality = higher
compression
jpeg_bytes.seek(0)
# Reload it for comparison
compressed_img = [Link](jpeg_bytes).convert('RGB')
Step 5: Calculate MSE, PSNR, SSIM
from [Link] import structural_similarity as ssim
from [Link] import rgb2gray
import math
# Convert images to NumPy arrays
original_np = [Link](original_img, dtype=np.float32)
compressed_np = [Link](compressed_img, dtype=np.float32)
# Ensure both images have same shape (in case resizing is needed)
compressed_np = compressed_np[:original_np.shape[0], :original_np.shape[1], :]
def mse(imageA, imageB):
return [Link]((imageA - imageB) ** 2)
def psnr(imageA, imageB):
err = mse(imageA, imageB)
if err == 0:
return float('inf')
PIXEL_MAX = 255.0
return 20 * math.log10(PIXEL_MAX / [Link](err))
# Convert to grayscale for SSIM
grayA = rgb2gray(original_np / 255.0)
grayB = rgb2gray(compressed_np / 255.0)
ssim_index, _ = ssim(grayA, grayB, full=True, data_range=1.0)
# Print the results
print(f"MSE: {mse(original_np, compressed_np):.2f}")
print(f"PSNR: {psnr(original_np, compressed_np):.2f} dB")
print(f"SSIM: {ssim_index:.4f}")
Step 6: Visual Comparison
import [Link] as plt
fig, axes = [Link](1, 3, figsize=(15, 5))
axes[0].imshow(original_img)
axes[0].set_title("Original (RAW RGB)")
axes[1].imshow(compressed_img)
axes[1].set_title("JPEG Compressed")
axes[2].imshow([Link](original_np - compressed_np).astype(np.uint8))
axes[2].set_title("Difference Map")
for ax in axes:
[Link]("off")
plt.tight_layout()
[Link]()
Summary
Metric Description Good Value
Average squared pixel
MSE Lower is better
error
>30 dB is
PSNR Signal-to-noise ratio
acceptable
SSIM Structural similarity Closer to 1 is better
More to do
• You can tweak the JPEG quality parameter to test how compression affects image
quality.
• RAW files differ in interpretation depending on camera models; rawpy handles most
standard formats.