Canny边缘检测算法原理与实现

目录

Canny边缘检测算法原理与实现

算法原理

Python实现代码

关键步骤详解

算法特点

实现代码

运行输出


Canny边缘检测算法原理与实现

Canny边缘检测是计算机视觉中最经典的边缘检测算法之一,由John F. Canny在1986年提出。下面我将详细介绍其原理,并提供Python实现代码。

算法原理

Canny边缘检测包含以下5个步骤:

  1. 高斯滤波:使用高斯滤波器平滑图像,减少噪声影响

  2. 计算梯度:使用Sobel算子计算图像的梯度幅值和方向

  3. 非极大值抑制:保留梯度幅值局部最大值,细化边缘

  4. 双阈值检测:使用高低阈值确定强边缘和弱边缘

  5. 边缘连接:通过滞后阈值处理连接边缘

Python实现代码

关键步骤详解

  1. 高斯滤波:

    • 使用高斯核平滑图像,减少噪声影响

    • 高斯核大小影响边缘检测的敏感度

  2. 梯度计算:

    • 使用Sobel算子计算x和y方向的梯度

    • 梯度幅值:G = sqrt(Gx² + Gy²)

    • 梯度方向:θ = arctan(Gy/Gx)

  3. 非极大值抑制:

    • 检查每个像素在梯度方向上的邻域

    • 只保留梯度幅值最大的像素,细化边缘

  4. 双阈值检测:

    • 高阈值:确定强边缘

    • 低阈值:确定弱边缘

    • 典型高低阈值比为1:2或1:3

  5. 边缘连接:

    • 弱边缘只有在连接到强边缘时才保留

    • 确保边缘的连续性

算法特点

  1. 优点:

    • 低错误率:尽可能少地检测非边缘点

    • 高定位性:检测的边缘点尽可能接近真实边缘

    • 最小响应:边缘点尽可能少,每个边缘只检测一次

  2. 参数选择:

    • 高斯核大小:影响平滑程度,通常5x5

    • 高低阈值:需要根据图像内容调整

    • Sobel核大小:通常3x3

这个实现展示了Canny边缘检测的核心原理,并与OpenCV的实现进行了比较。实际应用中,通常直接使用OpenCV的cv2.Canny()函数,因为它经过了高度优化。

实现代码

python 复制代码
import numpy as np
import cv2
import matplotlib.pyplot as plt

def canny_edge_detector(image, low_threshold=50, high_threshold=150, kernel_size=5):
    """
    手动实现Canny边缘检测算法
    
    参数:
        image: 输入灰度图像
        low_threshold: 低阈值
        high_threshold: 高阈值
        kernel_size: 高斯核大小
        
    返回:
        edges: 边缘检测结果
    """
    # 步骤1: 高斯滤波
    blurred = cv2.GaussianBlur(image, (kernel_size, kernel_size), 0)
    
    # 步骤2: 使用Sobel算子计算梯度
    grad_x = cv2.Sobel(blurred, cv2.CV_64F, 1, 0, ksize=3)
    grad_y = cv2.Sobel(blurred, cv2.CV_64F, 0, 1, ksize=3)
    
    # 计算梯度幅值和方向
    grad_magnitude = np.sqrt(grad_x**2 + grad_y**2)
    grad_direction = np.arctan2(grad_y, grad_x) * 180 / np.pi
    
    # 将角度规范化到0-180度
    grad_direction = np.mod(grad_direction, 180)
    
    # 步骤3: 非极大值抑制
    suppressed = non_maximum_suppression(grad_magnitude, grad_direction)
    
    # 步骤4和5: 双阈值检测和边缘连接
    edges = hysteresis_thresholding(suppressed, low_threshold, high_threshold)
    
    return edges

def non_maximum_suppression(grad_mag, grad_dir):
    """
    非极大值抑制
    """
    height, width = grad_mag.shape
    suppressed = np.zeros_like(grad_mag)
    
    for i in range(1, height-1):
        for j in range(1, width-1):
            angle = grad_dir[i, j]
            mag = grad_mag[i, j]
            
            # 将角度分类到最近的45度倍数
            if (0 <= angle < 22.5) or (157.5 <= angle <= 180):
                neighbor1 = grad_mag[i, j+1]
                neighbor2 = grad_mag[i, j-1]
            elif 22.5 <= angle < 67.5:
                neighbor1 = grad_mag[i+1, j-1]
                neighbor2 = grad_mag[i-1, j+1]
            elif 67.5 <= angle < 112.5:
                neighbor1 = grad_mag[i+1, j]
                neighbor2 = grad_mag[i-1, j]
            else:  # 112.5 <= angle < 157.5
                neighbor1 = grad_mag[i-1, j-1]
                neighbor2 = grad_mag[i+1, j+1]
            
            # 如果当前点是局部最大值,则保留
            if mag >= neighbor1 and mag >= neighbor2:
                suppressed[i, j] = mag
    
    return suppressed

def hysteresis_thresholding(image, low_threshold, high_threshold):
    """
    双阈值检测和边缘连接
    """
    height, width = image.shape
    result = np.zeros_like(image, dtype=np.uint8)
    
    # 标记强边缘(>=高阈值)和弱边缘(>=低阈值且<高阈值)
    strong_edges = (image >= high_threshold)
    weak_edges = (image >= low_threshold) & (image < high_threshold)
    
    # 强边缘直接保留
    result[strong_edges] = 255
    
    # 对于弱边缘,如果连接到强边缘则保留
    for i in range(1, height-1):
        for j in range(1, width-1):
            if weak_edges[i, j]:
                # 检查8邻域是否有强边缘
                if np.any(strong_edges[i-1:i+2, j-1:j+2]):
                    result[i, j] = 255
    
    return result

def compare_with_opencv(image_path='test_image.png'):
    """
    比较手动实现和OpenCV实现的Canny边缘检测
    """
    # 读取图像
    if image_path == 'test_image.png':
        # 如果没有图片,创建一个简单的测试图像
        img = np.zeros((300, 400), dtype=np.uint8)
        cv2.line(img, (50, 50), (350, 50), 255, 2)  # 水平线
        cv2.line(img, (50, 100), (350, 200), 255, 2)  # 斜线
        cv2.line(img, (200, 50), (200, 250), 255, 2)  # 垂直线
        cv2.line(img, (100, 200), (300, 200), 255, 2)  # 另一条水平线
    else:
        img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
    
    # 手动实现
    manual_edges = canny_edge_detector(img)
    
    # OpenCV实现
    opencv_edges = cv2.Canny(img, 50, 150)
    
    # 可视化比较
    plt.figure(figsize=(15, 5))
    
    plt.subplot(1, 3, 1)
    plt.imshow(img, cmap='gray')
    plt.title('原始图像')
    plt.axis('off')
    
    plt.subplot(1, 3, 2)
    plt.imshow(manual_edges, cmap='gray')
    plt.title('手动实现Canny')
    plt.axis('off')
    
    plt.subplot(1, 3, 3)
    plt.imshow(opencv_edges, cmap='gray')
    plt.title('OpenCV Canny')
    plt.axis('off')
    
    plt.tight_layout()
    plt.show()

# 测试代码
if __name__ == "__main__":
    compare_with_opencv()

运行输出

相关推荐
Java后端的Ai之路14 分钟前
Python 进阶探索30 - Python中的装饰器
开发语言·人工智能·python·文件处理·装饰器模式
打码的老程是远篁17 分钟前
手把手教你玩转大模型——3. Transformer 的位置编码——模型怎么知道谁在前,谁在后?
人工智能·算法
嘎嘎风18 分钟前
MiniMind 学习笔记之 06 一张图、一百行:MiniMind 的骨架
算法·源码
用户73416810354821 分钟前
只盯被动响应做状态监测?论“控制信号”在变工况故障诊断中的重要作用
算法
黑妹天下第一乖23 分钟前
第 05 讲:阿加犀 AidCV 图像处理加速与 OpenCV 一致开发实战
开发语言·图像处理·人工智能·嵌入式硬件·数码相机·opencv·计算机视觉
Ivanqhz23 分钟前
MQA、GQA、稀疏/滑动窗口注意力及混合范式
人工智能·算法·机器学习
言乐624 分钟前
Python基于关键词分拣快递(适用于电商与货运代理等)
开发语言·python·django·virtualenv·pygame
代码方舟24 分钟前
Python数据工程:利用天远名下车辆车牌查询A优化个人债务重组与清偿能力评估合规体验
人工智能·python
卷无止境25 分钟前
当AI写代码遇见Rust为什么会卡壳
后端·python
栈溢出了30 分钟前
LangGraph State、节点与动态路由学习笔记
python