Path: blob/main/latex-templates/templates/image-processing/edge_detection.tex
51 views
unlisted
% Edge Detection in Digital Image Processing1% Topics: Gradient operators, Canny edge detection, Laplacian methods, performance metrics2% Style: Technical report with computational implementations34\documentclass[a4paper, 11pt]{article}5\usepackage[utf8]{inputenc}6\usepackage[T1]{fontenc}7\usepackage{amsmath, amssymb}8\usepackage{graphicx}9\usepackage{siunitx}10\usepackage{booktabs}11\usepackage{subcaption}12\usepackage[makestderr]{pythontex}13\usepackage{algorithm}14\usepackage{algpseudocode}1516% Theorem environments17\newtheorem{definition}{Definition}[section]18\newtheorem{theorem}{Theorem}[section]19\newtheorem{example}{Example}[section]20\newtheorem{remark}{Remark}[section]2122\title{Edge Detection: From Gradient Operators to Multi-Scale Analysis}23\author{Computer Vision Laboratory}24\date{\today}2526\begin{document}27\maketitle2829\begin{abstract}30This report presents a comprehensive analysis of edge detection algorithms in digital image31processing. We examine gradient-based methods (Sobel, Prewitt, Roberts), the Canny edge32detector with its multi-stage pipeline, and Laplacian-based approaches including zero-crossing33detection. Computational implementations demonstrate edge detection on synthetic test patterns34and natural images, with quantitative performance evaluation using precision, recall, and35F-measure metrics. We analyze the trade-offs between noise sensitivity, localization accuracy,36and computational efficiency across different detector families.37\end{abstract}3839\section{Introduction}4041Edge detection is a fundamental operation in computer vision, providing critical information42about object boundaries, surface discontinuities, and scene structure. An edge represents a43significant local change in image intensity, typically occurring at the boundary between44regions of different brightness or texture.4546\begin{definition}[Edge]47An edge in a digital image is a set of connected pixels that form a boundary between two48regions with relatively distinct intensity properties. Mathematically, edges correspond to49local maxima in the intensity gradient magnitude:50\begin{equation}51|\nabla I| = \sqrt{\left(\frac{\partial I}{\partial x}\right)^2 + \left(\frac{\partial I}{\partial y}\right)^2}52\end{equation}53where $I(x,y)$ is the image intensity function.54\end{definition}5556\section{Theoretical Framework}5758\subsection{Gradient-Based Edge Detection}5960The image gradient captures the rate and direction of intensity change at each pixel.6162\begin{definition}[Image Gradient]63For a continuous image $I(x,y)$, the gradient is a vector:64\begin{equation}65\nabla I = \left[\frac{\partial I}{\partial x}, \frac{\partial I}{\partial y}\right]^T66\end{equation}67with magnitude $|\nabla I| = \sqrt{I_x^2 + I_y^2}$ and direction $\theta = \arctan(I_y/I_x)$.68\end{definition}6970\begin{theorem}[Sobel Operator]71The Sobel operator approximates the gradient using 3$\times$3 convolution kernels:72\begin{equation}73G_x = \begin{bmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{bmatrix}, \quad74G_y = \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ 1 & 2 & 1 \end{bmatrix}75\end{equation}76These kernels combine smoothing (perpendicular to gradient direction) with differentiation77(parallel to gradient direction), providing noise robustness.78\end{theorem}7980\begin{remark}[Alternative Gradient Operators]81Other first-order operators include:82\begin{itemize}83\item \textbf{Prewitt}: Equal-weight smoothing, kernels $\begin{bmatrix} -1 & 0 & 1 \\ -1 & 0 & 1 \\ -1 & 0 & 1 \end{bmatrix}$84\item \textbf{Roberts}: 2$\times$2 diagonal kernels $\begin{bmatrix} 1 & 0 \\ 0 & -1 \end{bmatrix}$, $\begin{bmatrix} 0 & 1 \\ -1 & 0 \end{bmatrix}$85\item \textbf{Scharr}: Optimized 3$\times$3 kernels with coefficients $\pm 3, \pm 10$86\end{itemize}87\end{remark}8889\subsection{Canny Edge Detector}9091The Canny algorithm (1986) remains the gold standard for edge detection due to its optimal92performance according to three criteria: good detection, good localization, and single response.9394\begin{theorem}[Canny Edge Detection Pipeline]95The Canny algorithm consists of four stages:96\begin{enumerate}97\item \textbf{Gaussian smoothing}: Convolve image with $G_\sigma(x,y) = \frac{1}{2\pi\sigma^2}e^{-(x^2+y^2)/2\sigma^2}$98\item \textbf{Gradient computation}: Calculate magnitude and orientation using Sobel or similar99\item \textbf{Non-maximum suppression}: Thin edges to single-pixel width by suppressing non-maxima perpendicular to edge direction100\item \textbf{Hysteresis thresholding}: Use dual thresholds $T_{low}$ and $T_{high}$ to link edge chains101\end{enumerate}102\end{theorem}103104\subsection{Laplacian-Based Methods}105106Second-order derivatives detect edges as zero-crossings rather than gradient maxima.107108\begin{definition}[Laplacian of Gaussian (LoG)]109The LoG operator combines Gaussian smoothing with the Laplacian:110\begin{equation}111\nabla^2 G_\sigma = -\frac{1}{\pi\sigma^4}\left[1 - \frac{x^2+y^2}{2\sigma^2}\right]e^{-(x^2+y^2)/2\sigma^2}112\end{equation}113Edges occur where $\nabla^2(G_\sigma * I) = 0$ with sign change.114\end{definition}115116\begin{remark}[Difference of Gaussians]117The LoG can be approximated by the Difference of Gaussians (DoG):118\begin{equation}119\text{DoG}(x,y) = G_{\sigma_1}(x,y) - G_{\sigma_2}(x,y), \quad \sigma_2 \approx 1.6\sigma_1120\end{equation}121This approximation is computationally efficient and forms the basis of SIFT features.122\end{remark}123124\subsection{Performance Evaluation}125126\begin{definition}[Edge Detection Metrics]127Given ground truth edges $E_{GT}$ and detected edges $E_{D}$:128\begin{itemize}129\item \textbf{Precision}: $P = \frac{TP}{TP + FP}$ (fraction of detected edges that are correct)130\item \textbf{Recall}: $R = \frac{TP}{TP + FN}$ (fraction of true edges detected)131\item \textbf{F-measure}: $F_1 = \frac{2PR}{P+R}$ (harmonic mean of precision and recall)132\item \textbf{Pratt's FOM}: $\text{FOM} = \frac{1}{\max(N_I, N_D)}\sum_{i=1}^{N_D}\frac{1}{1+\alpha d_i^2}$ where $d_i$ is distance to nearest true edge133\end{itemize}134\end{definition}135136\section{Computational Analysis}137138\begin{pycode}139import numpy as np140import matplotlib.pyplot as plt141from scipy import ndimage, signal142from scipy.ndimage import gaussian_filter, convolve143144np.random.seed(42)145146# Define gradient operators147sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])148sobel_y = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]])149150prewitt_x = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]])151prewitt_y = np.array([[-1, -1, -1], [0, 0, 0], [1, 1, 1]])152153roberts_x = np.array([[1, 0], [0, -1]])154roberts_y = np.array([[0, 1], [-1, 0]])155156# Create synthetic test image: step edges at known locations157img_size = 200158test_image = np.zeros((img_size, img_size))159# Vertical edge160test_image[:, img_size//2:] = 1.0161# Horizontal edge in right half162test_image[img_size//2:, img_size//2:] = 0.5163# Add diagonal edge164for i in range(img_size//2):165test_image[i, i:i+2] = 0.7166167# Add Gaussian noise168noise_level = 0.05169noisy_image = test_image + noise_level * np.random.randn(img_size, img_size)170noisy_image = np.clip(noisy_image, 0, 1)171172# Function implementations173def apply_sobel(image):174gradient_x = convolve(image, sobel_x)175gradient_y = convolve(image, sobel_y)176gradient_magnitude = np.sqrt(gradient_x**2 + gradient_y**2)177gradient_direction = np.arctan2(gradient_y, gradient_x)178return gradient_magnitude, gradient_direction, gradient_x, gradient_y179180def apply_prewitt(image):181gradient_x = convolve(image, prewitt_x)182gradient_y = convolve(image, prewitt_y)183gradient_magnitude = np.sqrt(gradient_x**2 + gradient_y**2)184return gradient_magnitude185186def non_maximum_suppression(gradient_mag, gradient_dir):187suppressed = np.zeros_like(gradient_mag)188angle = gradient_dir * 180.0 / np.pi189angle[angle < 0] += 180190191M, N = gradient_mag.shape192for i in range(1, M-1):193for j in range(1, N-1):194q = 255195r = 255196197# Angle 0 (horizontal)198if (0 <= angle[i,j] < 22.5) or (157.5 <= angle[i,j] <= 180):199q = gradient_mag[i, j+1]200r = gradient_mag[i, j-1]201# Angle 45202elif (22.5 <= angle[i,j] < 67.5):203q = gradient_mag[i+1, j-1]204r = gradient_mag[i-1, j+1]205# Angle 90 (vertical)206elif (67.5 <= angle[i,j] < 112.5):207q = gradient_mag[i+1, j]208r = gradient_mag[i-1, j]209# Angle 135210elif (112.5 <= angle[i,j] < 157.5):211q = gradient_mag[i-1, j-1]212r = gradient_mag[i+1, j+1]213214if (gradient_mag[i,j] >= q) and (gradient_mag[i,j] >= r):215suppressed[i,j] = gradient_mag[i,j]216217return suppressed218219def hysteresis_thresholding(image, threshold_low, threshold_high):220strong_edges = image > threshold_high221weak_edges = (image >= threshold_low) & (image <= threshold_high)222223edges = strong_edges.copy()224M, N = image.shape225226# Link weak edges to strong edges227for i in range(1, M-1):228for j in range(1, N-1):229if weak_edges[i, j]:230# Check 8-connected neighbors231if np.any(strong_edges[i-1:i+2, j-1:j+2]):232edges[i, j] = True233234return edges.astype(float)235236def canny_edge_detector(image, sigma=1.0, threshold_low=0.05, threshold_high=0.15):237# Step 1: Gaussian smoothing238smoothed = gaussian_filter(image, sigma)239240# Step 2: Gradient computation241gradient_mag, gradient_dir, _, _ = apply_sobel(smoothed)242243# Step 3: Non-maximum suppression244suppressed = non_maximum_suppression(gradient_mag, gradient_dir)245246# Step 4: Hysteresis thresholding247edges = hysteresis_thresholding(suppressed, threshold_low, threshold_high)248249return edges, gradient_mag, suppressed250251def laplacian_of_gaussian(image, sigma=2.0):252# Smooth with Gaussian253smoothed = gaussian_filter(image, sigma)254# Apply Laplacian255laplacian = ndimage.laplace(smoothed)256return laplacian257258def zero_crossing_detector(laplacian, threshold=0.01):259zero_crossings = np.zeros_like(laplacian)260M, N = laplacian.shape261262for i in range(1, M-1):263for j in range(1, N-1):264# Check for sign changes in 4-connected neighborhood265region = laplacian[i-1:i+2, j-1:j+2]266if (np.min(region) < -threshold) and (np.max(region) > threshold):267zero_crossings[i, j] = 1268269return zero_crossings270271# Apply different edge detectors272sobel_mag, sobel_dir, sobel_gx, sobel_gy = apply_sobel(noisy_image)273prewitt_mag = apply_prewitt(noisy_image)274canny_edges, canny_grad, canny_nms = canny_edge_detector(noisy_image, sigma=1.4,275threshold_low=0.05, threshold_high=0.15)276log_result = laplacian_of_gaussian(noisy_image, sigma=2.0)277log_edges = zero_crossing_detector(log_result, threshold=0.005)278279# Threshold gradient magnitudes for comparison280sobel_edges = sobel_mag > 0.2281prewitt_edges = prewitt_mag > 0.2282283# Count detected edges284num_sobel_edges = np.sum(sobel_edges)285num_prewitt_edges = np.sum(prewitt_edges)286num_canny_edges = np.sum(canny_edges)287num_log_edges = np.sum(log_edges)288289# Calculate ground truth edges from clean image290true_edges_v = np.zeros_like(test_image)291true_edges_v[:, img_size//2-1:img_size//2+1] = 1292true_edges_h = np.zeros_like(test_image)293true_edges_h[img_size//2-1:img_size//2+1, img_size//2:] = 1294ground_truth = (true_edges_v + true_edges_h) > 0295296# Compute performance metrics for Canny297def compute_metrics(detected, ground_truth, tolerance=2):298# Dilate ground truth for tolerance299from scipy.ndimage import binary_dilation300struct = np.ones((2*tolerance+1, 2*tolerance+1))301gt_dilated = binary_dilation(ground_truth, structure=struct)302303true_positive = np.sum(detected & gt_dilated)304false_positive = np.sum(detected & ~gt_dilated)305false_negative = np.sum(~detected & ground_truth)306307precision = true_positive / (true_positive + false_positive) if (true_positive + false_positive) > 0 else 0308recall = true_positive / (true_positive + false_negative) if (true_positive + false_negative) > 0 else 0309f_measure = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0310311return precision, recall, f_measure312313canny_precision, canny_recall, canny_f1 = compute_metrics(canny_edges, ground_truth)314sobel_precision, sobel_recall, sobel_f1 = compute_metrics(sobel_edges, ground_truth)315316# Create comprehensive visualization317fig = plt.figure(figsize=(16, 14))318319# Plot 1: Original test image320ax1 = fig.add_subplot(4, 4, 1)321ax1.imshow(test_image, cmap='gray', interpolation='nearest')322ax1.set_title('Clean Test Image', fontsize=10)323ax1.axis('off')324325# Plot 2: Noisy image326ax2 = fig.add_subplot(4, 4, 2)327ax2.imshow(noisy_image, cmap='gray', interpolation='nearest')328ax2.set_title(f'Noisy Image (SNR$\\approx${1/noise_level:.1f})', fontsize=10)329ax2.axis('off')330331# Plot 3: Sobel gradient magnitude332ax3 = fig.add_subplot(4, 4, 3)333im3 = ax3.imshow(sobel_mag, cmap='hot', interpolation='nearest')334ax3.set_title('Sobel Gradient Magnitude', fontsize=10)335ax3.axis('off')336plt.colorbar(im3, ax=ax3, fraction=0.046)337338# Plot 4: Gradient direction339ax4 = fig.add_subplot(4, 4, 4)340im4 = ax4.imshow(sobel_dir, cmap='hsv', interpolation='nearest')341ax4.set_title('Gradient Direction', fontsize=10)342ax4.axis('off')343plt.colorbar(im4, ax=ax4, fraction=0.046)344345# Plot 5: Sobel X gradient346ax5 = fig.add_subplot(4, 4, 5)347im5 = ax5.imshow(sobel_gx, cmap='RdBu_r', interpolation='nearest')348ax5.set_title('Sobel $G_x$ (Vertical Edges)', fontsize=10)349ax5.axis('off')350plt.colorbar(im5, ax=ax5, fraction=0.046)351352# Plot 6: Sobel Y gradient353ax6 = fig.add_subplot(4, 4, 6)354im6 = ax6.imshow(sobel_gy, cmap='RdBu_r', interpolation='nearest')355ax6.set_title('Sobel $G_y$ (Horizontal Edges)', fontsize=10)356ax6.axis('off')357plt.colorbar(im6, ax=ax6, fraction=0.046)358359# Plot 7: Sobel thresholded360ax7 = fig.add_subplot(4, 4, 7)361ax7.imshow(sobel_edges, cmap='gray', interpolation='nearest')362ax7.set_title(f'Sobel Edges ({num_sobel_edges} pixels)', fontsize=10)363ax7.axis('off')364365# Plot 8: Prewitt comparison366ax8 = fig.add_subplot(4, 4, 8)367ax8.imshow(prewitt_edges, cmap='gray', interpolation='nearest')368ax8.set_title(f'Prewitt Edges ({num_prewitt_edges} pixels)', fontsize=10)369ax8.axis('off')370371# Plot 9: Canny - gradient after smoothing372ax9 = fig.add_subplot(4, 4, 9)373im9 = ax9.imshow(canny_grad, cmap='hot', interpolation='nearest')374ax9.set_title('Canny: Gradient (Smoothed)', fontsize=10)375ax9.axis('off')376plt.colorbar(im9, ax=ax9, fraction=0.046)377378# Plot 10: Canny - after NMS379ax10 = fig.add_subplot(4, 4, 10)380im10 = ax10.imshow(canny_nms, cmap='hot', interpolation='nearest')381ax10.set_title('Canny: Non-Max Suppression', fontsize=10)382ax10.axis('off')383plt.colorbar(im10, ax=ax10, fraction=0.046)384385# Plot 11: Canny final edges386ax11 = fig.add_subplot(4, 4, 11)387ax11.imshow(canny_edges, cmap='gray', interpolation='nearest')388ax11.set_title(f'Canny Edges ({num_canny_edges} pixels)', fontsize=10)389ax11.axis('off')390391# Plot 12: Canny overlay392ax12 = fig.add_subplot(4, 4, 12)393overlay = np.stack([noisy_image, noisy_image, noisy_image], axis=-1)394overlay[canny_edges.astype(bool), :] = [1, 0, 0]395ax12.imshow(overlay, interpolation='nearest')396ax12.set_title('Canny Overlay on Image', fontsize=10)397ax12.axis('off')398399# Plot 13: Laplacian of Gaussian400ax13 = fig.add_subplot(4, 4, 13)401im13 = ax13.imshow(log_result, cmap='RdBu_r', interpolation='nearest')402ax13.set_title('Laplacian of Gaussian', fontsize=10)403ax13.axis('off')404plt.colorbar(im13, ax=ax13, fraction=0.046)405406# Plot 14: LoG zero crossings407ax14 = fig.add_subplot(4, 4, 14)408ax14.imshow(log_edges, cmap='gray', interpolation='nearest')409ax14.set_title(f'LoG Zero-Crossings ({num_log_edges} pixels)', fontsize=10)410ax14.axis('off')411412# Plot 15: Performance comparison413ax15 = fig.add_subplot(4, 4, 15)414methods = ['Sobel', 'Canny']415precision_vals = [sobel_precision, canny_precision]416recall_vals = [sobel_recall, canny_recall]417f1_vals = [sobel_f1, canny_f1]418419x_pos = np.arange(len(methods))420width = 0.25421422ax15.bar(x_pos - width, precision_vals, width, label='Precision', color='steelblue', edgecolor='black')423ax15.bar(x_pos, recall_vals, width, label='Recall', color='coral', edgecolor='black')424ax15.bar(x_pos + width, f1_vals, width, label='F-measure', color='lightgreen', edgecolor='black')425426ax15.set_ylabel('Score', fontsize=10)427ax15.set_title('Detection Performance', fontsize=10)428ax15.set_xticks(x_pos)429ax15.set_xticklabels(methods, fontsize=9)430ax15.legend(fontsize=8)431ax15.set_ylim([0, 1])432ax15.grid(axis='y', alpha=0.3)433434# Plot 16: Edge count comparison435ax16 = fig.add_subplot(4, 4, 16)436edge_counts = [num_sobel_edges, num_prewitt_edges, num_canny_edges, num_log_edges]437count_labels = ['Sobel', 'Prewitt', 'Canny', 'LoG']438colors_bars = ['steelblue', 'coral', 'lightgreen', 'plum']439440ax16.bar(count_labels, edge_counts, color=colors_bars, edgecolor='black')441ax16.set_ylabel('Number of Edge Pixels', fontsize=10)442ax16.set_title('Edge Detector Comparison', fontsize=10)443ax16.tick_params(axis='x', labelsize=9)444ax16.grid(axis='y', alpha=0.3)445446plt.tight_layout()447plt.savefig('edge_detection_comprehensive.pdf', dpi=150, bbox_inches='tight')448plt.close()449\end{pycode}450451\begin{figure}[htbp]452\centering453\includegraphics[width=\textwidth]{edge_detection_comprehensive.pdf}454\caption{Comprehensive edge detection analysis: (a) Clean synthetic test image with step edges;455(b) Image corrupted with Gaussian noise; (c-d) Sobel gradient magnitude and direction revealing456edge strength and orientation; (e-f) Directional gradients $G_x$ and $G_y$ highlighting vertical457and horizontal features; (g-h) Thresholded Sobel and Prewitt edges showing similar detection458patterns; (i-l) Canny detector pipeline stages from gradient computation through non-maximum459suppression to final hysteresis thresholding with overlay; (m-n) Laplacian of Gaussian response460and zero-crossing detection; (o-p) Quantitative performance comparison showing Canny's superior461precision-recall balance and total edge pixel counts across methods.}462\label{fig:edge_detection}463\end{figure}464465\section{Algorithm Implementations}466467\subsection{Canny Edge Detector}468469\begin{algorithm}470\caption{Canny Edge Detection}471\begin{algorithmic}[1]472\Require Image $I$, Gaussian $\sigma$, thresholds $T_{low}$, $T_{high}$473\Ensure Binary edge map $E$474\State $I_{smooth} \gets G_\sigma * I$ \Comment{Gaussian smoothing}475\State $G_x \gets$ Sobel$_x * I_{smooth}$476\State $G_y \gets$ Sobel$_y * I_{smooth}$477\State $M \gets \sqrt{G_x^2 + G_y^2}$ \Comment{Gradient magnitude}478\State $\theta \gets \arctan(G_y / G_x)$ \Comment{Gradient direction}479\State $M_{nms} \gets$ NonMaxSuppress$(M, \theta)$ \Comment{Thin edges}480\State $E \gets$ HysteresisThreshold$(M_{nms}, T_{low}, T_{high})$ \Comment{Link edges}481\State \Return $E$482\end{algorithmic}483\end{algorithm}484485\subsection{Non-Maximum Suppression}486487The key to single-pixel-wide edges is suppressing gradient magnitudes that are not local maxima488in the direction perpendicular to the edge.489490\begin{algorithm}491\caption{Non-Maximum Suppression}492\begin{algorithmic}[1]493\Require Gradient magnitude $M$, direction $\theta$494\Ensure Suppressed magnitude $M_{nms}$495\For{each pixel $(i,j)$}496\State Quantize $\theta_{i,j}$ to nearest 45$^\circ$ (0, 45, 90, 135)497\State Get neighboring pixels $p, q$ along gradient direction498\If{$M_{i,j} \geq M_p$ \textbf{and} $M_{i,j} \geq M_q$}499\State $M_{nms}[i,j] \gets M_{i,j}$ \Comment{Local maximum}500\Else501\State $M_{nms}[i,j] \gets 0$ \Comment{Suppressed}502\EndIf503\EndFor504\State \Return $M_{nms}$505\end{algorithmic}506\end{algorithm}507508\section{Natural Image Analysis}509510\begin{pycode}511# Create a more complex natural-like scene512np.random.seed(123)513scene_size = 300514515# Create synthetic natural scene with multiple objects516scene = np.ones((scene_size, scene_size)) * 0.3517518# Add geometric shapes with different intensities519# Circle520center_y, center_x = 100, 100521radius = 40522y, x = np.ogrid[:scene_size, :scene_size]523circle_mask = (x - center_x)**2 + (y - center_y)**2 <= radius**2524scene[circle_mask] = 0.8525526# Rectangle527scene[150:220, 50:150] = 0.6528529# Triangle (approximate)530for i in range(70):531scene[200-i, 180:180+2*i] = 0.45532533# Add texture using random noise patterns534texture = np.random.randn(scene_size, scene_size) * 0.03535scene += texture536scene = np.clip(scene, 0, 1)537538# Add realistic noise539scene_noisy = scene + 0.02 * np.random.randn(scene_size, scene_size)540scene_noisy = np.clip(scene_noisy, 0, 1)541542# Apply multiple edge detectors with different parameters543canny_sigma1, _, _ = canny_edge_detector(scene_noisy, sigma=0.8, threshold_low=0.04, threshold_high=0.12)544canny_sigma2, _, _ = canny_edge_detector(scene_noisy, sigma=1.5, threshold_low=0.04, threshold_high=0.12)545canny_sigma3, _, _ = canny_edge_detector(scene_noisy, sigma=2.5, threshold_low=0.04, threshold_high=0.12)546547sobel_scene, _, _, _ = apply_sobel(scene_noisy)548log_scene = laplacian_of_gaussian(scene_noisy, sigma=1.5)549log_scene_edges = zero_crossing_detector(log_scene, threshold=0.003)550551# Multi-scale analysis552scales = [0.5, 1.0, 2.0, 3.0]553multiscale_edges = np.zeros_like(scene)554555for sigma in scales:556edges, _, _ = canny_edge_detector(scene_noisy, sigma=sigma, threshold_low=0.04, threshold_high=0.12)557multiscale_edges += edges558559multiscale_edges = multiscale_edges > 0560561# Create visualization562fig2 = plt.figure(figsize=(16, 10))563564# Original scene565ax1 = fig2.add_subplot(3, 4, 1)566ax1.imshow(scene, cmap='gray', interpolation='nearest')567ax1.set_title('Synthetic Scene (Clean)', fontsize=10)568ax1.axis('off')569570# Noisy scene571ax2 = fig2.add_subplot(3, 4, 2)572ax2.imshow(scene_noisy, cmap='gray', interpolation='nearest')573ax2.set_title('Noisy Scene', fontsize=10)574ax2.axis('off')575576# Sobel magnitude577ax3 = fig2.add_subplot(3, 4, 3)578im3 = ax3.imshow(sobel_scene, cmap='hot', interpolation='nearest')579ax3.set_title('Sobel Gradient Magnitude', fontsize=10)580ax3.axis('off')581plt.colorbar(im3, ax=ax3, fraction=0.046)582583# Canny sigma=0.8584ax4 = fig2.add_subplot(3, 4, 4)585ax4.imshow(canny_sigma1, cmap='gray', interpolation='nearest')586ax4.set_title(f'Canny ($\\sigma$=0.8, {np.sum(canny_sigma1)} edges)', fontsize=10)587ax4.axis('off')588589# Canny sigma=1.5590ax5 = fig2.add_subplot(3, 4, 5)591ax5.imshow(canny_sigma2, cmap='gray', interpolation='nearest')592ax5.set_title(f'Canny ($\\sigma$=1.5, {np.sum(canny_sigma2)} edges)', fontsize=10)593ax5.axis('off')594595# Canny sigma=2.5596ax6 = fig2.add_subplot(3, 4, 6)597ax6.imshow(canny_sigma3, cmap='gray', interpolation='nearest')598ax6.set_title(f'Canny ($\\sigma$=2.5, {np.sum(canny_sigma3)} edges)', fontsize=10)599ax6.axis('off')600601# LoG response602ax7 = fig2.add_subplot(3, 4, 7)603im7 = ax7.imshow(log_scene, cmap='RdBu_r', interpolation='nearest')604ax7.set_title('LoG Response ($\\sigma$=1.5)', fontsize=10)605ax7.axis('off')606plt.colorbar(im7, ax=ax7, fraction=0.046)607608# LoG edges609ax8 = fig2.add_subplot(3, 4, 8)610ax8.imshow(log_scene_edges, cmap='gray', interpolation='nearest')611ax8.set_title(f'LoG Edges ({np.sum(log_scene_edges)} pixels)', fontsize=10)612ax8.axis('off')613614# Multi-scale edges615ax9 = fig2.add_subplot(3, 4, 9)616ax9.imshow(multiscale_edges, cmap='gray', interpolation='nearest')617ax9.set_title('Multi-Scale Edges (Combined)', fontsize=10)618ax9.axis('off')619620# Edge count vs sigma621ax10 = fig2.add_subplot(3, 4, 10)622sigma_range = np.linspace(0.5, 4.0, 15)623edge_counts_sigma = []624for sig in sigma_range:625edges_temp, _, _ = canny_edge_detector(scene_noisy, sigma=sig, threshold_low=0.04, threshold_high=0.12)626edge_counts_sigma.append(np.sum(edges_temp))627628ax10.plot(sigma_range, edge_counts_sigma, 'o-', linewidth=2, markersize=6, color='steelblue')629ax10.set_xlabel('Gaussian $\\sigma$', fontsize=10)630ax10.set_ylabel('Edge Pixels Detected', fontsize=10)631ax10.set_title('Scale Parameter Effect', fontsize=10)632ax10.grid(True, alpha=0.3)633634# Threshold sensitivity635ax11 = fig2.add_subplot(3, 4, 11)636threshold_range = np.linspace(0.02, 0.20, 15)637edge_counts_thresh = []638for thresh in threshold_range:639edges_temp, _, _ = canny_edge_detector(scene_noisy, sigma=1.5,640threshold_low=thresh, threshold_high=thresh*3)641edge_counts_thresh.append(np.sum(edges_temp))642643ax11.plot(threshold_range, edge_counts_thresh, 's-', linewidth=2, markersize=6, color='coral')644ax11.set_xlabel('Threshold $T_{low}$', fontsize=10)645ax11.set_ylabel('Edge Pixels Detected', fontsize=10)646ax11.set_title('Threshold Sensitivity', fontsize=10)647ax11.grid(True, alpha=0.3)648649# Histogram of gradient magnitudes650ax12 = fig2.add_subplot(3, 4, 12)651gradient_flat = sobel_scene.flatten()652ax12.hist(gradient_flat, bins=50, density=True, alpha=0.7, color='steelblue', edgecolor='black')653ax12.axvline(x=0.12, color='red', linestyle='--', linewidth=2, label='Typical Threshold')654ax12.set_xlabel('Gradient Magnitude', fontsize=10)655ax12.set_ylabel('Probability Density', fontsize=10)656ax12.set_title('Gradient Distribution', fontsize=10)657ax12.legend(fontsize=9)658ax12.set_xlim([0, 0.5])659660plt.tight_layout()661plt.savefig('edge_detection_natural_scene.pdf', dpi=150, bbox_inches='tight')662plt.close()663664# Store key statistics665num_canny_sigma1 = int(np.sum(canny_sigma1))666num_canny_sigma2 = int(np.sum(canny_sigma2))667num_canny_sigma3 = int(np.sum(canny_sigma3))668num_log_scene = int(np.sum(log_scene_edges))669num_multiscale = int(np.sum(multiscale_edges))670\end{pycode}671672\begin{figure}[htbp]673\centering674\includegraphics[width=\textwidth]{edge_detection_natural_scene.pdf}675\caption{Edge detection on synthetic natural scene: (a-b) Clean and noisy synthetic scenes676containing geometric shapes with varying intensities; (c) Sobel gradient magnitude showing677continuous edge strength; (d-f) Canny edge detection at three scales ($\sigma$ = 0.8, 1.5, 2.5)678demonstrating the noise-detail trade-off, detecting \py{num_canny_sigma1}, \py{num_canny_sigma2},679and \py{num_canny_sigma3} edge pixels respectively; (g-h) Laplacian of Gaussian response and680zero-crossing detection finding \py{num_log_scene} edge pixels; (i) Multi-scale edge combination681yielding \py{num_multiscale} consolidated edge pixels; (j-k) Parameter sensitivity analysis showing682edge count dependence on Gaussian smoothing scale and threshold values; (l) Gradient magnitude683histogram revealing the distribution of edge strengths and typical threshold selection criterion.}684\label{fig:natural_scene}685\end{figure}686687\section{Results and Performance Analysis}688689\subsection{Computational Complexity}690691\begin{pycode}692print(r"\begin{table}[htbp]")693print(r"\centering")694print(r"\caption{Edge Detection Algorithm Complexity}")695print(r"\begin{tabular}{lccc}")696print(r"\toprule")697print(r"Algorithm & Convolutions & Non-linear Ops & Complexity \\")698print(r"\midrule")699print(r"Sobel & 2 ($3\times3$) & Sqrt, Atan & $O(n^2)$ \\")700print(r"Prewitt & 2 ($3\times3$) & Sqrt, Atan & $O(n^2)$ \\")701print(r"Roberts & 2 ($2\times2$) & Sqrt, Atan & $O(n^2)$ \\")702print(r"Canny & 3 (Gaussian + 2 Sobel) & NMS + Hysteresis & $O(n^2)$ \\")703print(r"LoG & 1 (LoG kernel) & Zero-crossing & $O(n^2)$ \\")704print(r"\bottomrule")705print(r"\end{tabular}")706print(r"\label{tab:complexity}")707print(r"\end{table}")708\end{pycode}709710\subsection{Detection Performance Summary}711712\begin{pycode}713print(r"\begin{table}[htbp]")714print(r"\centering")715print(r"\caption{Edge Detector Performance on Test Image}")716print(r"\begin{tabular}{lcccc}")717print(r"\toprule")718print(r"Method & Edges Detected & Precision & Recall & F-measure \\")719print(r"\midrule")720print(f"Sobel (threshold=0.2) & {num_sobel_edges} & {sobel_precision:.3f} & {sobel_recall:.3f} & {sobel_f1:.3f} \\\\")721print(f"Prewitt (threshold=0.2) & {num_prewitt_edges} & --- & --- & --- \\\\")722print(f"Canny ($\\sigma$=1.4) & {num_canny_edges} & {canny_precision:.3f} & {canny_recall:.3f} & {canny_f1:.3f} \\\\")723print(f"LoG ($\\sigma$=2.0) & {num_log_edges} & --- & --- & --- \\\\")724print(r"\bottomrule")725print(r"\end{tabular}")726print(r"\label{tab:performance}")727print(r"\end{table}")728\end{pycode}729730\subsection{Scale Parameter Analysis}731732The choice of Gaussian smoothing scale $\sigma$ represents a fundamental trade-off in edge detection:733734\begin{itemize}735\item \textbf{Small $\sigma$ (0.5-1.0)}: Preserves fine details but sensitive to noise, detects $\sim$\py{num_canny_sigma1} edges736\item \textbf{Medium $\sigma$ (1.5-2.5)}: Balanced noise suppression and detail retention, detects $\sim$\py{num_canny_sigma2} edges737\item \textbf{Large $\sigma$ (3.0-4.0)}: Strong noise rejection but loses fine structure, detects $\sim$\py{num_canny_sigma3} edges738\end{itemize}739740\section{Discussion}741742\begin{example}[Gradient Operator Selection]743For real-time applications on noisy images, the Sobel operator provides an excellent balance:744\begin{itemize}745\item \textbf{Efficiency}: Only two $3\times3$ convolutions required746\item \textbf{Robustness}: Built-in smoothing suppresses high-frequency noise747\item \textbf{Accuracy}: Weighted gradient approximation superior to simple differences748\end{itemize}749For highest quality, Canny's multi-stage pipeline achieves F-measure of \py{f"{canny_f1:.3f}"}750compared to Sobel's \py{f"{sobel_f1:.3f}"} on our test image.751\end{example}752753\begin{remark}[Second vs First Derivatives]754Laplacian-based methods offer theoretical advantages:755\begin{itemize}756\item Isotropic response (rotation invariant)757\item Precise localization at zero-crossings758\item No explicit thresholding of gradient magnitude759\end{itemize}760However, second derivatives amplify noise more severely, requiring larger smoothing kernels761and potentially missing low-contrast edges.762\end{remark}763764\begin{example}[Hysteresis Thresholding]765Canny's dual-threshold approach solves the edge linking problem:766\begin{itemize}767\item High threshold ($T_h$): Seeds strong edge segments (high confidence)768\item Low threshold ($T_l$): Extends edges along weak gradients (connected to strong edges)769\item Typical ratio: $T_h/T_l \approx 2-3$770\end{itemize}771This prevents edge fragmentation while rejecting isolated noise pixels.772\end{example}773774\section{Conclusions}775776This analysis demonstrates the implementation and evaluation of fundamental edge detection algorithms:777778\begin{enumerate}779\item Gradient-based methods (Sobel, Prewitt) detect \py{num_sobel_edges} and \py{num_prewitt_edges}780edge pixels respectively on the noisy test image781\item The Canny detector's multi-stage pipeline achieves superior performance with precision782\py{f"{canny_precision:.3f}"}, recall \py{f"{canny_recall:.3f}"}, and F-measure \py{f"{canny_f1:.3f}"}783\item Laplacian of Gaussian methods provide rotation-invariant detection with \py{num_log_edges}784zero-crossings identified785\item Scale selection critically impacts detection: smaller $\sigma$ values preserve detail786(\py{num_canny_sigma1} edges at $\sigma$=0.8) while larger values enhance noise robustness787(\py{num_canny_sigma3} edges at $\sigma$=2.5)788\item Multi-scale approaches combining detections across $\sigma \in [0.5, 3.0]$ yield789\py{num_multiscale} consolidated edge pixels790\end{enumerate}791792The choice of edge detector depends on application requirements: Sobel for computational efficiency,793Canny for accuracy and completeness, and LoG for precise localization in low-noise scenarios.794795\section*{References}796797\begin{enumerate}798\item Canny, J. (1986). A computational approach to edge detection. \textit{IEEE Trans. Pattern Analysis and Machine Intelligence}, 8(6), 679-698.799\item Sobel, I., \& Feldman, G. (1968). A 3x3 isotropic gradient operator for image processing. \textit{Stanford Artificial Intelligence Project}.800\item Marr, D., \& Hildreth, E. (1980). Theory of edge detection. \textit{Proc. Royal Society of London B}, 207(1167), 187-217.801\item Prewitt, J. M. S. (1970). Object enhancement and extraction. \textit{Picture Processing and Psychopictorics}, Academic Press.802\item Roberts, L. G. (1965). Machine perception of three-dimensional solids. \textit{Optical and Electro-Optical Information Processing}, MIT Press.803\item Torre, V., \& Poggio, T. A. (1986). On edge detection. \textit{IEEE Trans. Pattern Analysis and Machine Intelligence}, 8(2), 147-163.804\item Haralick, R. M. (1984). Digital step edges from zero crossing of second directional derivatives. \textit{IEEE Trans. Pattern Analysis and Machine Intelligence}, 6(1), 58-68.805\item Scharr, H. (2000). Optimal operators in digital image processing. \textit{PhD Thesis}, University of Heidelberg.806\item Elder, J. H., \& Zucker, S. W. (1998). Local scale control for edge detection and blur estimation. \textit{IEEE Trans. Pattern Analysis and Machine Intelligence}, 20(7), 699-716.807\item Lindeberg, T. (1998). Edge detection and ridge detection with automatic scale selection. \textit{Int. Journal of Computer Vision}, 30(2), 117-156.808\item Ziou, D., \& Tabbone, S. (1998). Edge detection techniques: An overview. \textit{International Journal of Pattern Recognition and Image Analysis}, 8(4), 537-559.809\item Heath, M., et al. (1997). A robust visual method for assessing the relative performance of edge-detection algorithms. \textit{IEEE Trans. Pattern Analysis and Machine Intelligence}, 19(12), 1338-1359.810\item Kittler, J. (1983). On the accuracy of the Sobel edge detector. \textit{Image and Vision Computing}, 1(1), 37-42.811\item Deriche, R. (1987). Using Canny's criteria to derive a recursively implemented optimal edge detector. \textit{Int. Journal of Computer Vision}, 1(2), 167-187.812\item Bovik, A. C., Huang, T. S., \& Munson, D. C. (1983). A generalization of median filtering using linear combinations of order statistics. \textit{IEEE Trans. Acoustics, Speech, and Signal Processing}, 31(6), 1342-1350.813\item Pratt, W. K. (2007). \textit{Digital Image Processing}, 4th ed. Wiley-Interscience.814\item Gonzalez, R. C., \& Woods, R. E. (2018). \textit{Digital Image Processing}, 4th ed. Pearson.815\item Jain, A. K. (1989). \textit{Fundamentals of Digital Image Processing}. Prentice Hall.816\end{enumerate}817818\end{document}819820821