Practical 2: Impact of Sampling Rate on Spatial Resolution
Run this practical in Google Colab
Objective
To evaluate the relationship between the sampling rate (N) and spatial resolution in digital images.
2. Description / Theory
Problem Statement: Spatial resolution represents the size of the smallest perceptible detail in an image and is fundamentally dependent on the sampling rate. A decrease in the number of samples (N) results in a loss of geometric detail. In this experiment, you will simulate progressive spatial resolution reduction and analyze the resulting artifacts.
# ----------------------------------------------------------------------
# Install dependencies (uncomment for Google Colab)
# !pip install opencv-python-headless matplotlib numpy
import cv2
import numpy as np
import matplotlib.pyplot as plt
import os
# === AUTO-DOWNLOAD DATASET (works in Google Colab and locally) ===
import os, urllib.request, zipfile
# Choose which chapter to download (CH01-CH12)
CHAPTER = "CH02" # Chapter 2: Digital Image Fundamentals
DATASET_PATH = f"datasets/{CHAPTER}/"
DOWNLOAD_BASE = "https://www.imageprocessingplace.com/downloads_V3/dip3e_downloads/dip3e_book_images"
if not os.path.exists(DATASET_PATH) or not any(f.endswith('.tif') for f in os.listdir(DATASET_PATH)):
zip_name = f"DIP3E_{CHAPTER}_Original_Images.zip"
url = f"{DOWNLOAD_BASE}/{zip_name}"
print(f"Downloading {CHAPTER} dataset from imageprocessingplace.com...")
urllib.request.urlretrieve(url, "chapter.zip")
os.makedirs(DATASET_PATH, exist_ok=True)
with zipfile.ZipFile("chapter.zip", "r") as z:
for f in z.namelist():
if f.lower().endswith(".tif"):
fname = os.path.basename(f)
if fname:
with z.open(f) as src, open(os.path.join(DATASET_PATH, fname), "wb") as dst:
dst.write(src.read())
os.remove("chapter.zip")
print(f"Downloaded {len([f for f in os.listdir(DATASET_PATH) if f.endswith('.tif')])} images")
else:
print(f"Dataset ready: {len([f for f in os.listdir(DATASET_PATH) if f.endswith('.tif')])} images")
# List all available images
images = sorted([f for f in os.listdir(DATASET_PATH) if f.endswith('.tif')])
print(f"\nAvailable images ({len(images)}):")
for i, name in enumerate(images, 1):
print(f" {i}. {name}")
# === SELECT YOUR IMAGE HERE ===
selected_image = "Fig0222(b)(cameraman).tif" # Change this to any image
# Load the image
img = cv2.imread(os.path.join(DATASET_PATH, selected_image))
# Convert BGR to Grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
print(f"Original shape: {gray.shape}")
# Establish baseline resolution: 1024 x 1024
baseline = cv2.resize(gray, (1024, 1024), interpolation=cv2.INTER_LINEAR)
print(f"Baseline resolution: {baseline.shape}")
plt.figure(figsize=(6, 6))
plt.imshow(baseline, cmap='gray')
plt.title(f"Baseline: 1024 x 1024")
plt.axis('off')
plt.show()
# Progressive downsampling
current = baseline.copy()
resolutions = [(1024, 1024)] # Store all resolutions
downsampled_images = [baseline.copy()] # Store all images
for i in range(5):
h, w = current.shape
new_h = h // 2
new_w = w // 2
current = cv2.resize(current, (new_w, new_h), interpolation=cv2.INTER_AREA)
resolutions.append((new_h, new_w))
downsampled_images.append(current.copy())
plt.figure(figsize=(5, 5))
plt.imshow(current, cmap='gray')
plt.title(f"Step {i+1}: {new_w} x {new_h}")
plt.axis('off')
plt.show()
print(f"Step {i+1}: Resolution = {new_w} x {new_h}")
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
labels = ["1024x1024", "512x512", "256x256", "128x128", "64x64", "32x32"]
for ax, img_ds, label in zip(axes.flatten(), downsampled_images, labels):
ax.imshow(img_ds, cmap='gray')
ax.set_title(label, fontsize=12, fontweight='bold')
ax.axis('off')
plt.suptitle(f"Progressive Downsampling: {selected_image}", fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
for ax, img_ds, label in zip(axes.flatten(), downsampled_images, labels):
# Resize back to 1024x1024 to see quality loss
upscaled = cv2.resize(img_ds, (1024, 1024), interpolation=cv2.INTER_LINEAR)
ax.imshow(upscaled, cmap='gray')
ax.set_title(f"From {label} → 1024x1024", fontsize=11)
ax.axis('off')
plt.suptitle("Quality Loss: Downsampled then Upscaled Back to 1024x1024", fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
Part 1: Progressive Downsampling
The image is resized to a 1024x1024 baseline, then progressively halved 5 times using cv2.INTER_AREA interpolation: 1024 → 512 → 256 → 128 → 64 → 32.
Part 2: Comparison Plot
View all resolution levels in a single comparison figure.
Part 3: Quality Loss Visualization
Each downsampled image is resized back to 1024x1024 to reveal the quality degradation at each level.
Analysis Questions
- Artifact Identification: At which resolution (N) do "jagged lines" (aliasing) become visible to the eye? Explain how this correlates to the sampling rate.
- Sensitivity Analysis: Theory suggests spatial resolution is more sensitive to shape variations than lighting variations. Compare the detail in textured areas vs. smooth areas; which loses perceptible detail first?
- DPI Correlation: If the physical size of the image remains constant while the pixel count is halved, how does this affect the Dots Per Inch (DPI)? Relate this to the "blocky" appearance.