边缘检测的100种方法

文章目录

什么是边缘检测 ?

边缘检测:边缘检测是图像处理中的一种技术,旨在识别和定位图像中亮度变化显著的区域。这些变化通常表现为强度、颜色或纹理的急剧变动,通常对应于物体的边界或轮廓。通过检测这些边缘信息,边缘检测能够帮助提取图像的结构特征,促进图像的分析和处理。

边缘检测的目的

(1)特征提取:帮助提取图像中重要的结构特征,便于进一步的分析和处理。

(2)图像分割:通过识别边缘来区分图像中的不同区域,实现图像的分割。

(3)增强图像质量:通过突出重要特征,提高图像的可读性和可分析性。
边缘检测的应用

(1)图像分析:用于对象检测、跟踪和识别。

(2)医学影像:在CT、MRI等医学图像中用于识别和分析结构。

(3)计算机视觉:为自动驾驶、视频监控等应用提供关键数据。

(4)图形设计:用于图像的自动轮廓提取和处理。

边缘检测的性能通常通过准确度鲁棒性(对噪声的抵抗力)计算效率来评估。选择合适的边缘检测算法需考虑具体应用的需求和图像特征。

一、边缘检测算子:Sobel算子、Scharr算子、Laplacian算子、Canny算子

OpenCV图像处理(全)

python 复制代码
import cv2  # opencv    读取图像默认为BGR
import tifffile

image = tifffile.imread(r"F:\py\gray_image.tif")
################################################################################
sobel_x = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3)  # 计算x方向的导数
sobel_y = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3)  # 计算y方向的导数
sobel_magnitude = cv2.magnitude(sobel_x, sobel_y)  # 计算梯度幅值
sobel_magnitude_Abs = cv2.convertScaleAbs(sobel_magnitude)
################################################################################
scharr_x = cv2.Scharr(image, cv2.CV_64F, 1, 0)  # 计算x方向的导数
scharr_y = cv2.Scharr(image, cv2.CV_64F, 0, 1)  # 计算y方向的导数
scharr_magnitude = cv2.magnitude(scharr_x, scharr_y)  # 计算梯度幅值
scharr_magnitude_Abs = cv2.convertScaleAbs(scharr_magnitude)  # 计算绝对值
################################################################################
laplacian = cv2.Laplacian(image, cv2.CV_64F)
laplacian_Abs = cv2.convertScaleAbs(laplacian)
################################################################################
image_canny50_100 = cv2.Canny(image, 50, 100)
image_canny100_200 = cv2.Canny(image, 100, 200)
################################################################################
import napari
viewer = napari.Viewer()  # 创建napari视图
viewer.add_image(image, name="image")
viewer.add_image(sobel_magnitude_Abs, name="sobel_magnitude_Abs")
viewer.add_image(scharr_magnitude_Abs, name="scharr_magnitude_Abs")
viewer.add_image(laplacian_Abs, name="laplacian_Abs")
viewer.add_image(image_canny50_100, name="image_canny50_100")
viewer.add_image(image_canny100_200, name="image_canny100_200")
viewer.grid.enabled = True  # 启用网格视图模式
napari.run()  # 启动napari事件循环,使得窗口保持打开并可交互。

二、梯度计算 + 顶帽 + 黑帽 + 拉普拉斯金字塔

python 复制代码
import cv2                          # opencv    读取图像默认为BGR
import numpy as np
import tifffile

image = tifffile.imread(r"F:\py\gray_image.tif")
################################################################################
kernel = np.ones((10, 10), np.uint8)  # 初始化卷积核(np.ones: 生成一个数值全为1的3x3数组)
image_grad = cv2.morphologyEx(image, cv2.MORPH_GRADIENT, kernel)   # 梯度计算
image_top  = cv2.morphologyEx(image, cv2.MORPH_TOPHAT, kernel)     # 顶帽运算
image_black = cv2.morphologyEx(image, cv2.MORPH_BLACKHAT, kernel)  # 黑帽运算

image_up = cv2.pyrUp(image)            # 高斯金字塔:图像上采样(放大一倍)
image_up_down = cv2.pyrDown(image_up)  # 拉普拉斯金字塔:先上采样,再下采样
image_up_down_resize = cv2.resize(image_up_down, (image.shape[1], image.shape[0]))  # 确保尺度一致
image_up_down_laplacian = cv2.subtract(image, image_up_down_resize)  # 拉普拉斯金字塔:当前层减去上采样

image_down = cv2.pyrDown(image)        # 高斯金字塔:图像下采样(缩小一倍)
image_down_up = cv2.pyrUp(image_down)  # 拉普拉斯金字塔:先下采样,再上采样
image_down_up_resize = cv2.resize(image_down_up, (image.shape[1], image.shape[0]))  # 确保尺度一致
image_down_up_laplacian = cv2.subtract(image, image_down_up_resize)  # 拉普拉斯金字塔:当前层减去上采样
################################################################################
import napari
viewer = napari.Viewer()  # 创建napari视图
viewer.add_image(image, name="image")
viewer.add_image(image_grad, name="image_grad")
viewer.add_image(image_top, name="image_top")
viewer.add_image(image_black, name="image_black")
viewer.add_image(image_up_down_laplacian, name="image_up_down_laplacian")
viewer.add_image(image_down_up_laplacian, name="image_down_up_laplacian")
viewer.grid.enabled = True  # 启用网格视图模式
napari.run()  # 启动napari事件循环,使得窗口保持打开并可交互。

三、相位一致性(Phase Congruency,PC)

相位一致性(Phase Congruency),用于检测图像中的结构信息,如边缘、角点、纹理等。

3.1、底层代码(2D)

Phase Based Feature Detection and Phase Congruency

python 复制代码
import sys
sys.dont_write_bytecode = True  # Python 解释器在运行脚本时不生成 .pyc 文件
#####################################################################
import math
import numpy as np
import cv2  # Faster Fourier transforms than NumPy and Scikit-Image
import tifffile


def phase_congruency(input_image, num_scales, num_angles):
    """
    函数说明:计算图像的相位一致性
    输入参数:
            input_image: 输入图像,应为二维的NumPy数组。
            num_scales: 环形带通滤波器的尺度数量。如:4
            num_angles: 环形带通滤波器的角度数量。如:6
    输出参数:M(归一化的相位一致性矩阵,在0~1范围之间)
    """
    ############################################################################################################
    # (1)初始化参数和变量
    min_wave_length = 3   # 最小波长,用于滤波器设计
    scale_multiplier = 2.1  # 滤波器比例因子
    gaussian_std = 0.55  # 频率域高斯滤波器的标准差
    noise_threshold_param = 2.0  # 噪声阈值参数
    sigmoid_cutoff = 0.5  # Sigmoidal加权函数的截断值
    sigmoid_slope = 10  # Sigmoidal加权函数的斜率
    noise_estimation_method = -1  # 噪声估计方法(-1表示使用median方法)
    epsilon = .0001  # 防止分母为零的小值
    ############################################################################################################
    # (2)构建环形带通滤波器:由多个尺度的Log-Gabor滤波器和角度高斯函数组合而成,在不同的频率和方向上对图像进行滤波。
    f_transform = cv2.dft(np.float32(input_image), flags=cv2.DFT_COMPLEX_OUTPUT)  # 离散傅里叶变换,得到频域复数图像
    nrows, ncols = input_image.shape  # 获取输入图像的行数和列数

    # 初始化一些中间结果矩阵和累加器
    zero_matrix = np.zeros((nrows, ncols))
    complex_response = np.zeros((nrows, ncols, num_scales, num_angles), dtype=complex)
    phase_congruency_matrix = np.zeros((nrows, ncols, num_angles))
    cov_x2 = np.zeros((nrows, ncols))
    cov_y2 = np.zeros((nrows, ncols))
    cov_xy = np.zeros((nrows, ncols))
    energy_vector = np.zeros((nrows, ncols, 3))
    pc_sum = np.zeros((nrows, ncols))

    # 矩阵的半径
    cy = math.floor(nrows / 2)  # 图像中心的y坐标
    cx = math.floor(ncols / 2)  # 图像中心的x坐标
    y_coords, x_coords = np.mgrid[0:nrows, 0:ncols]
    y_coords = (y_coords - cy) / nrows  # 归一化y坐标,以中心为原点
    x_coords = (x_coords - cx) / ncols  # 归一化x坐标,以中心为原点
    radius_matrix = np.sqrt(x_coords ** 2 + y_coords ** 2)  # 构建图像中每个像素点到中心的距离矩阵
    radius_matrix[cy, cx] = 1  # 中心点的半径设为1,避免分母为零
    theta_matrix = np.arctan2(-y_coords, x_coords)  # 构建每个像素点到中心的角度矩阵(范围:-pi 到 pi)
    sin_theta = np.sin(theta_matrix)  # sin(theta)矩阵
    cos_theta = np.cos(theta_matrix)  # cos(theta)矩阵

    # 初始化一组环形带通滤波器
    annular_bandpass_filters = np.empty((nrows, ncols, num_scales))

    # 设置滤波器参数
    d_theta_on_sigma = 1.3
    filter_orientations = np.arange(start=0, stop=math.pi - math.pi / num_angles, step=math.pi / num_angles)

    # 角度高斯函数的标准偏差,用于在频率面构建滤波器
    theta_sigma = math.pi / num_angles / d_theta_on_sigma

    # 初始化滤波器和奇偶波的变量
    bandpass_filters = np.empty((nrows, ncols, num_scales, num_angles))
    even_wavelets = np.empty((nrows, ncols, num_scales, num_angles))
    odd_wavelets = np.empty((nrows, ncols, num_scales, num_angles))

    # 实现对数Gabor传递函数的方法
    filter_order = 15  # filter 'sharpness'
    lowcut = .45
    norm_radius = radius_matrix / (abs(x_coords).max() * 2)
    lowpass_butterworth = 1.0 / (1.0 + (norm_radius / lowcut) ** (2 * filter_order))

    for s in np.arange(num_scales):
        wavelength = min_wave_length * scale_multiplier ** s  # 计算每个尺度的波长
        center_frequency = 1.0 / wavelength  # 计算每个尺度的中心频率
        log_gabor_filter = np.exp((-(np.log(radius_matrix / center_frequency)) ** 2) / (2 * math.log(gaussian_std) ** 2))  # 计算环形带通滤波器
        annular_bandpass_filters[:, :, s] = log_gabor_filter * lowpass_butterworth  # 应用低通滤波器
        annular_bandpass_filters[cy, cx, s] = 0  # 将中心点设为零,去除直流分量
    ############################################################################################################
    # (3)循环处理不同方向的滤波器:对于每个角度方向,将图像与滤波器进行卷积,并计算响应的幅度和相位。然后,根据幅度和相位计算能量。
    for o in np.arange(num_angles):
        angle = o * math.pi / num_angles  # 计算当前方向的滤波角度
        ds = sin_theta * math.cos(angle) - cos_theta * math.sin(angle)  # 计算sin差异
        dc = cos_theta * math.cos(angle) + sin_theta * math.sin(angle)  # 计算cos差异
        dtheta = np.abs(np.arctan2(ds, dc))  # 计算绝对角距离
        dtheta = np.minimum(dtheta * num_angles / 2, math.pi)  # 归一化角距离到0到pi之间
        spread = (np.cos(dtheta) + 1) / 2  # 计算角度分布权重,归一化到0到1之间

        # 初始化累加器矩阵
        sum_even_response = np.zeros((nrows, ncols))
        sum_odd_response = np.zeros((nrows, ncols))
        sum_amplitude_response = np.zeros((nrows, ncols))
        energy = np.zeros((nrows, ncols))
        max_amplitude = []
        # 循环处理不同尺度的滤波器
        for s in np.arange(num_scales):
            # 使用角度分布权重乘以径向和角度滤波器得到滤波器
            current_filter = annular_bandpass_filters[:, :, s] * spread

            # 对滤波器进行傅里叶变换
            critical_filter_shift = np.fft.ifftshift(current_filter)
            critical_filter_shift_cv = np.empty((nrows, ncols, 2))
            for ip in range(2):
                critical_filter_shift_cv[:, :, ip] = critical_filter_shift

            # 使用滤波器对输入图像进行卷积,得到奇偶响应结果
            matrix_eo = cv2.idft(critical_filter_shift_cv * f_transform)
            complex_response[:, :, s, o] = matrix_eo[:, :, 1] + 1j * matrix_eo[:, :, 0]

            # 计算奇偶响应结果的振幅
            amplitude_response = cv2.magnitude(matrix_eo[:, :, 0], matrix_eo[:, :, 1])

            # 累加振幅响应结果
            sum_amplitude_response += amplitude_response
            sum_even_response += matrix_eo[:, :, 1]
            sum_odd_response += matrix_eo[:, :, 0]

            # 在最小尺度下,通过振幅响应结果估计噪声特性
            if s == 0:
                tau = np.median(sum_amplitude_response) / math.sqrt(math.log(4))  # 使用中值法估计噪声统计信息
                max_amplitude = amplitude_response
            else:
                max_amplitude = np.maximum(max_amplitude, amplitude_response)  # 记录跨尺度的最大分量的振幅。这是确定频率扩展加权的必要信息。

        # 累加奇偶响应的能量
        energy_vector[:, :, 0] += sum_even_response
        energy_vector[:, :, 1] += math.cos(angle) * sum_odd_response
        energy_vector[:, :, 2] += math.sin(angle) * sum_odd_response

        # 计算能量
        X_energy = np.sqrt(sum_even_response ** 2 + sum_odd_response ** 2) + epsilon
        mean_even_response = sum_even_response / X_energy
        mean_odd_response = sum_odd_response / X_energy

        for s in np.arange(num_scales):
            E = complex_response[:, :, s, o].real  # 提取实部
            O = complex_response[:, :, s, o].imag  # 提取虚部
            energy += E * mean_even_response + O * mean_odd_response - np.abs(E * mean_odd_response - O * mean_even_response)

        ############################################################################################################
        # (4)去除噪声:估计噪声水平,并根据噪声阈值去除一部分低能量信号(噪声和弱特征)。
        total_tau = tau * (1 - (1 / scale_multiplier) ** num_scales) / (1 - (1 / scale_multiplier))
        est_noise_mean = total_tau * math.sqrt(math.pi / 2)
        est_noise_sigma = total_tau * math.sqrt((4 - math.pi) / 2)
        threshold = est_noise_mean + noise_threshold_param * est_noise_sigma  # 根据噪声均值和标准差估计噪声
        energy = np.maximum(energy - threshold, 0)  # 去除噪声

        # 根据振幅响应计算能量
        width = (sum_amplitude_response / (max_amplitude + epsilon) - 1) / (num_scales - 1)
        weight = 1.0 / (1 + np.exp((sigmoid_cutoff - width) * sigmoid_slope))  # Sigmoidal加权函数
        phase_congruency_matrix[:, :, o] = weight * energy / sum_amplitude_response  # 归一化相位一致性
        pc_sum += phase_congruency_matrix[:, :, o]  # 累加到最终相位一致性

        # 计算协方差
        cov_x = phase_congruency_matrix[:, :, o] * math.cos(angle)
        cov_y = phase_congruency_matrix[:, :, o] * math.sin(angle)
        cov_x2 += cov_x ** 2
        cov_y2 += cov_y ** 2
        cov_xy += cov_x * cov_y

    ############################################################################################################
    # (5)计算相位一致性:根据不同方向的滤波器响应,计算相位一致性的协方差矩阵,然后求得最大矩和最小矩。
    cov_x2 /= (num_angles / 2)
    cov_y2 /= (num_angles / 2)
    cov_xy = 4 * cov_xy / num_angles
    denominator = np.sqrt(cov_xy ** 2 + (cov_x2 - cov_y2) ** 2) + epsilon
    max_energy_matrix = (cov_y2 + cov_x2 + denominator) / 2  # 归一化最大值
    min_energy_matrix = (cov_y2 + cov_x2 - denominator) / 2  # 归一化最小值
    ############################################################################################################
    # (6)计算图像特征(方向和特征相位):根据滤波器响应的能量分量,计算主方向和特征相位。
    orientation_matrix = np.arctan2(energy_vector[:, :, 2], energy_vector[:, :, 1])  # 方向矩阵
    orientation_matrix[orientation_matrix < 0] += math.pi  # 确保方向值为正
    orientation_matrix = np.round(orientation_matrix * 180 / math.pi)  # 方向转为度

    odd_vector_magnitude = np.sqrt(energy_vector[:, :, 1] ** 2 + energy_vector[:, :, 2] ** 2)  # 奇偶向量的模
    feature_type = np.arctan2(energy_vector[:, :, 0], odd_vector_magnitude)  # 特征类型(也可用于方向性分析)

    return max_energy_matrix  # 归一化后的相位一致性矩阵


if __name__ == '__main__':
    image = tifffile.imread(r"F:\py\gray_image.tif")
    image_pc = phase_congruency(image, num_scales=4, num_angles=6)

    import napari
    viewer = napari.Viewer()  # 创建napari视图
    viewer.add_image(image, name="image")
    viewer.add_image(image_pc, name="phase_congruency")
    viewer.grid.enabled = True  # 启用网格视图模式
    napari.run()  # 启动napari事件循环,使得窗口保持打开并可交互。

3.2、skimage.feature(2D / 3D)

相位一致性:skimage.feature.hessian_matrix() + skimage.feature.hessian_matrix_eigvals()

python 复制代码
import numpy as np
import tifffile
import napari

import skimage.feature
import skimage.filters


def calculate_phase_congruency(input_image, canny_thresholds=(50, 200), sigma=2.0, phase_power=2.0, amplification_factor=2.0):
    """计算图像的相位一致性特征,并与 Canny 边缘检测结果合成。
        :param input_image: 输入图像,支持2D或3D。
        :param phase_power: 相位幂,用于控制相位一致性的非线性调节。
        :param amplification_factor: 放大因子,用于相位一致性特征增强。
        :param sigma: Hessian 矩阵的平滑参数。
        :param canny_thresholds: Canny 边缘检测的低阈值和高阈值。
    :return: 相位一致性和 Canny 边缘检测合成图。
    """
    # (1)中值滤波以减少噪声
    filtered_image = skimage.filters.median(input_image)

    # (2)Canny 边缘检(2D / 3D)
    if filtered_image.ndim == 2:
        edges = skimage.feature.canny(filtered_image,
                                      low_threshold=canny_thresholds[0],
                                      high_threshold=canny_thresholds[1])
    elif filtered_image.ndim == 3:
        edges = np.zeros_like(filtered_image)
        for slice_index in range(filtered_image.shape[0]):
            edges[slice_index] = skimage.feature.canny(filtered_image[slice_index],
                                                       low_threshold=canny_thresholds[0],
                                                       high_threshold=canny_thresholds[1])
    else:
        raise ValueError("Only 2D or 3D images are supported.")

    # (3)计算 Hessian 矩阵和特征值
    hessian_matrices = skimage.feature.hessian_matrix(filtered_image, sigma=sigma, mode='reflect')
    eigenvalues = skimage.feature.hessian_matrix_eigvals(hessian_matrices)

    # (4)计算相位一致性特征图(2D / 3D)
    if filtered_image.ndim == 2:
        lambda1, lambda2 = eigenvalues
        phase_congruency_map = np.sqrt(lambda1 ** 2 + lambda2 ** 2) / \
                               (np.abs(lambda1) + np.abs(lambda2) + 1e-8)
    elif filtered_image.ndim == 3:
        lambda1, lambda2, lambda3 = eigenvalues * -1
        phase_congruency_map = np.sqrt(lambda1 ** 2 + lambda2 ** 2 + lambda3 ** 2) / \
                               (np.abs(lambda1) + np.abs(lambda2) + np.abs(lambda3) + 1e-8)
    # (5)合成 Canny 边缘检测与相位一致性特征
    combined_phase_congruency = (phase_congruency_map * 0.5 + edges) ** phase_power * amplification_factor

    import napari
    viewer = napari.Viewer()  # 创建napari视图
    viewer.add_image(image, name="image")
    viewer.add_image(edges, name="edges")
    viewer.add_image(phase_congruency_map, name="phase_congruency_map")
    viewer.add_image(combined_phase_congruency, name="combined_phase_congruency")
    viewer.grid.enabled = True  # 启用网格视图模式
    napari.run()  # 启动napari事件循环,使得窗口保持打开并可交互。

    return combined_phase_congruency


if __name__ == '__main__':
    image = tifffile.imread(r"F:\py\gray_image.tif")
    image_pc = calculate_phase_congruency(image)
相关推荐
算法小白(真小白)3 小时前
低代码软件搭建自学第二天——构建拖拽功能
python·低代码·pyqt
唐小旭3 小时前
服务器建立-错误:pyenv环境建立后python版本不对
运维·服务器·python
007php0073 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
Chinese Red Guest3 小时前
python
开发语言·python·pygame
骑个小蜗牛4 小时前
Python 标准库:string——字符串操作
python
黄公子学安全6 小时前
Java的基础概念(一)
java·开发语言·python
程序员一诺7 小时前
【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】
后端·python
小木_.7 小时前
【Python 图片下载器】一款专门为爬虫制作的图片下载器,多线程下载,速度快,支持续传/图片缩放/图片压缩/图片转换
爬虫·python·学习·分享·批量下载·图片下载器
Jiude7 小时前
算法题题解记录——双变量问题的 “枚举右,维护左”
python·算法·面试