Histogram Equalization & Edge Detection techniques using Python and OpenCV
This tutorial walks you through two important tasks in image processing. In Task 1, you will learn how to implement histogram equalization from scratch to enhance the contrast of a grayscale image. Task 2 compares the performance of Sobel and Canny edge detection methods on images with simple and complex edges using OpenCV.
Task 1: Histogram Equalization from Scratch
This task enhances the contrast of a grayscale image by redistributing its intensity values without using OpenCV's built-in functions.
Step 1: Import Required Libraries
import matplotlib.pyplot as plt
import numpy as np
import time
import cv2
Step 2: Define Helper Functions
These functions load the image, compute its histogram, the cumulative distribution function (CDF), normalize the CDF, and finally perform histogram equalization.
def load_image(path):
"""Load image using matplotlib"""
return plt.imread(path)
def compute_histogram(image):
"""Compute histogram of grayscale image"""
histogram = np.zeros(256)
for i in range(image.shape[0]):
for j in range(image.shape[1]):
intensity = image[i, j]
intensity = int(round(intensity))
intensity = min(max(0, intensity), 255)
histogram[intensity] += 1
return histogram
def compute_cdf(histogram):
"""Compute cumulative distribution function"""
cdf = np.zeros(257)
cdf[0] = 0
for i in range(1, 257):
cdf[i] = cdf[i-1] + histogram[i-1]
return cdf
def normalize_cdf(cdf):
"""Normalize CDF to [0, 255] range"""
min_non_zero = np.min(cdf[np.where(cdf > 0)])
max_value = cdf[-1]
if max_value == 0:
return cdf
normalized_cdf = (cdf - min_non_zero) * 255 / (max_value - min_non_zero)
return normalized_cdf
def histogram_equalization(image):
"""Perform histogram equalization on grayscale image"""
histogram = compute_histogram(image)
cdf = compute_cdf(histogram)
normalized_cdf = normalize_cdf(cdf)
# Map each pixel using normalized CDF
equalized_image = np.zeros_like(image)
for i in range(image.shape[0]):
for j in range(image.shape[1]):
equalized_image[i, j] = normalized_cdf[image[i, j].astype(int)]
return equalized_image
Step 3: Load the Image and Apply Histogram Equalization
# Load image and convert to grayscale
image_path = '/content/img01.jpg'
original = load_image(image_path)
original_gray = np.dot(original[...,:3], [0.2989, 0.5870, 0.1140]) # Convert to grayscale
# Perform histogram equalization
equalized = histogram_equalization(original_gray)
# Compute histograms for original and equalized images
original_hist = compute_histogram(original_gray.astype(np.uint8))
equalized_hist = compute_histogram(equalized.astype(np.uint8))
Step 4: Save and Display the Results
def save_image(image, title, filename):
"""Save the image to a file"""
plt.imsave(filename, image, cmap='gray')
print(f'Saved {title} as {filename}')
def display_results(original, equalized, original_hist, equalized_hist):
"""Display original and equalized images with histograms"""
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
axes[0,0].imshow(original, cmap='gray')
axes[0,0].set_title('Original Image')
axes[0,1].imshow(equalized, cmap='gray')
axes[0,1].set_title('Equalized Image')
axes[1,0].bar(range(256), original_hist)
axes[1,0].set_title('Original Histogram')
axes[1,1].bar(range(256), equalized_hist)
axes[1,1].set_title('Equalized Histogram')
plt.tight_layout()
plt.savefig('task1_overview.png')
plt.show()
# Save images and histograms
save_image(original_gray, 'Original Image', 'original_image.png')
save_image(equalized, 'Equalized Image', 'equalized_image.png')
plt.figure(figsize=(6, 4))
plt.bar(range(256), original_hist)
plt.title('Original Histogram')
plt.savefig('original_histogram.png')
plt.close()
plt.figure(figsize=(6, 4))
plt.bar(range(256), equalized_hist)
plt.title('Equalized Histogram')
plt.savefig('equalized_histogram.png')
plt.close()
# Display final results
display_results(original_gray, equalized, original_hist, equalized_hist)
Task 2: Comparing Sobel and Canny Edge Detection
This task compares the performance of Sobel and Canny edge detection methods on images with simple and complex edges using OpenCV.
Step 1: Define Edge Detection Functions
def sobel_edge_detection(image):
"""Apply Sobel edge detection using OpenCV built-in functions"""
grad_x = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3)
grad_y = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3)
abs_grad_x = cv2.convertScaleAbs(grad_x)
abs_grad_y = cv2.convertScaleAbs(grad_y)
grad = cv2.addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0)
_, thresholded = cv2.threshold(grad, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
return thresholded
def canny_edge_detection(image):
"""Apply Canny edge detection"""
return cv2.Canny(image, 50, 150)
Step 2: Load Images and Apply Edge Detection
# Paths for images with simple and complex edges
simple_image_path = '/content/img03.png'
complex_image_path = '/content/img02.jpg'
# Load images in grayscale
simple_image = cv2.imread(simple_image_path, 0)
complex_image = cv2.imread(complex_image_path, 0)
# Apply Sobel edge detection and measure execution time
import time
start_time = time.time()
sobel_simple = sobel_edge_detection(simple_image)
sobel_simple_time = time.time() - start_time
start_time = time.time()
sobel_complex = sobel_edge_detection(complex_image)
sobel_complex_time = time.time() - start_time
# Apply Canny edge detection and measure execution time
start_time = time.time()
canny_simple = canny_edge_detection(simple_image)
canny_simple_time = time.time() - start_time
start_time = time.time()
canny_complex = canny_edge_detection(complex_image)
canny_complex_time = time.time() - start_time
Step 3: Display and Analyze Edge Detection Results
def display_edge_detection_results(image, sobel, canny, title):
"""Display edge detection results"""
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
axes[0].imshow(image, cmap='gray')
axes[0].set_title(f'Original {title} Image')
axes[1].imshow(sobel, cmap='gray')
axes[1].set_title('Sobel Edges')
axes[2].imshow(canny, cmap='gray')
axes[2].set_title('Canny Edges')
plt.savefig(f"task{title}.png")
plt.show()
# Display results for both simple and complex images
display_edge_detection_results(simple_image, sobel_simple, canny_simple, 'Simple')
display_edge_detection_results(complex_image, sobel_complex, canny_complex, 'Complex')
# Print execution times
print(f'Sobel Edge Detection (Simple Image): {sobel_simple_time:.4f} seconds')
print(f'Sobel Edge Detection (Complex Image): {sobel_complex_time:.4f} seconds')
print(f'Sobel Edge Detection (Total): {sobel_simple_time + sobel_complex_time:.4f} seconds')
print(f'Canny Edge Detection (Simple Image): {canny_simple_time:.4f} seconds')
print(f'Canny Edge Detection (Complex Image): {canny_complex_time:.4f} seconds')
print(f'Canny Edge Detection (Total): {canny_simple_time + canny_complex_time:.4f} seconds')
# Analysis and Comparison
print("\nAnalysis and Comparison of Sobel and Canny Edge Detection Methods:")
print("1. Edge Clarity and Continuity:")
print("- Sobel: Good for simple edges, may have broken edges in complex images.")
print("- Canny: Better at preserving continuous edges, especially in complex images.")
print("\n2. Sensitivity to Noise:")
print("- Sobel: More sensitive to noise due to its linear operation.")
print("- Canny: Less sensitive due to Gaussian smoothing and hysteresis.")
print("\n3. Computational Efficiency:")
print(f"- Sobel: {sobel_simple_time + sobel_complex_time:.4f}s")
print(f"- Canny: {canny_simple_time + canny_complex_time:.4f}s")
Conclusion
This tutorial demonstrated how to enhance image contrast using histogram equalization implemented from scratch and compared two popular edge detection methods—Sobel and Canny—using OpenCV. Experiment with these methods and parameters to optimize performance for different types of images.


Comments
Post a Comment