一些数字图像处理知识

1 minute read

Published:

记录一些数字图像处理的知识

图像边缘检测

边缘

  • 边缘存在于目标与背景、目标与目标、区域与区域之间,是图像最基本的特征之一,为人们描述或识别目标以及解释图像提供了一个重要的特征参数。
  • 边缘常常意味着一个区域的终结和另一个区域的开始,是图像局部特征不连续的表现。
  • 图像边缘有方向幅度两个特征。
  • 如果能成功地检测出图像的边缘,图像分析、图像识别就会方便得多,精确度也会得到提高。

边缘检测的基本思想

图1

在边缘的一阶导数中,幅度峰值一般对应的就是边缘的位置。在图像由暗变亮的突变位置有一个整的峰值,而在图像由亮变暗的位置会有一个负的峰值,在其他的位置都为0,因此可以使用一阶导数的幅度值来检测边缘的存在

或者,我们也可以使用二阶导数的中的过零点来检测边缘的存在。

边缘检测的基本步骤

图2

  • 滤波:边缘检测算法主要是基于图像强度的一阶和二阶导数,但导数的计算对噪声很敏感,因此必须使用滤波器来改善与噪声有关的边缘检测器的性能。

大多数滤波器在降低噪声的同时也导致了边缘强度的损失,因此,增强边缘和降低噪声之间需要折中。

  • 增强:增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将邻域(或局部)强度值有显著变化的点突显出来。边缘增强一般是通过计算梯度幅值来完成的
  • 检测:在图像中有许多点的梯度幅值比较大,而这些点在特定的应用领域中并不都是边缘,所以应该用某种方法来确定哪些点是边缘点。最简单的边缘检测判据是梯度幅值阈值判据。
  • 定位:如果某一应用场合要求确定边缘位置,则边缘的位置可在子像素分辨率上来估计,边缘的方位也可以被估计出来。

利用算子去计算导数

一阶导数:通过梯度算子进行计算。

二阶导数:通过Laplacian算子进行计算。

Roberts梯度算子法

Roberts梯度算子法属于一阶导数的梯度算子计算,其实就是采用对角方向相邻两像素之差,故也称为4点差分法。其水平和垂直方向的梯度定义为:

\[\begin{cases}G_h(m,n)=f(m,n)-f(m-1,n-1)\\G_v(m,n)=f(m,n-1)-f(m-1,n)\end{cases}\]

写成矩阵的梯度模板可表示为:

\[W_h=\begin{pmatrix}-1&0&0\\0&1&0\\0&0&0\end{pmatrix}\quad W_v=\begin{pmatrix}0&-1&0\\1&0&0\\0&0&0\end{pmatrix}\]

特点是:用4分点进行差分以求得梯度,方法简单。缺点是对噪声较敏感,常用于不含噪声的图像边缘点检测。

Roberts实现代码

import cv2
import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt

# 读取图片
image = cv2.imread('aaa.jpg')

# 将图片转换为灰度图
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Roberts算子
roberts_cross_v = np.array([[1, 0],
                            [0, -1]])

roberts_cross_h = np.array([[0, 1],
                            [-1, 0]])

# 使用卷积应用Roberts算子
vertical_edges = ndimage.convolve(gray_image, roberts_cross_v)
horizontal_edges = ndimage.convolve(gray_image, roberts_cross_h)

# 计算边缘强度
edge_magnitude = np.sqrt(np.square(horizontal_edges) + np.square(vertical_edges))

# 转换到0-255范围
edge_magnitude *= 255.0 / edge_magnitude.max()

# 使用matplotlib显示原图和边缘检测结果
plt.figure(figsize=(10, 5))
plt.subplot(121), plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)), plt.title('Original Image')
plt.subplot(122), plt.imshow(edge_magnitude, cmap='gray'), plt.title('Edge Detection')
plt.show()

Roberts检测效果

图3

平滑梯度算子法(平均差分法)

平滑梯度算子方法有两种

  • Prewitt梯度算子法(平均差分法)

​因为平均能减少或消除噪声,Prewitt梯度算子法就是先求平均,再求差分来计算梯度。它的水平和垂直梯度模板分别为:

\[\begin{array}{rcl}W_{h}&=&\frac{1}{3}\begin{pmatrix}-1&0&1\\-1&0&1\\-1&0&1\end{pmatrix}&&W_{\nu}&=&\frac{1}{3}\begin{pmatrix}-1&-1&-1\\0&0&0\\1&1&1\end{pmatrix}\end{array}\]

利用检测模板可求得水平和垂直方向的梯度,再通过梯度合成和边缘点判定,即可得到平均差分法的检测结果。

  • Sobel算子法

Sobel算子就是对当前行或列对应的值加权后,再进行平均和差分,也称为加权平均差分。水平和垂直梯度模板分别为:

\[W_h=\frac14\begin{pmatrix}-1&0&1\\-2&0&2\\-1&0&1\end{pmatrix}\quad W_\nu=\frac14\begin{pmatrix}-1&-2&-1\\0&0&0\\1&2&1\end{pmatrix}\]

Sobel算子和Prewitt算子一样,都在检测边缘点的同时具有抑制噪声的能力,检测出的边缘宽度至少为二像素。由于它们都是先平均后差分,平均时会丢失一些细节信息,使边缘有一定模糊。但由于Sobel算子的加权作用,其使边缘模糊的程度要低于Prewitt**算子。

Prewitt梯度算子边缘检测代码

import cv2
import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt

# 读取图片
image = cv2.imread('aaa.jpg')

# 将图片转换为灰度图
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Prewitt算子
prewitt_vertical = np.array([[-1, 0, 1],
                             [-1, 0, 1],
                             [-1, 0, 1]]) * (1/3)

prewitt_horizontal = np.array([[-1, -1, -1],
                               [0, 0, 0],
                               [1, 1, 1]]) * (1/3)

# 使用卷积应用Prewitt算子
vertical_edges = ndimage.convolve(gray_image, prewitt_vertical)
horizontal_edges = ndimage.convolve(gray_image, prewitt_horizontal)

# 计算边缘强度
edge_magnitude = np.sqrt(np.square(horizontal_edges) + np.square(vertical_edges))

# 转换到0-255范围
edge_magnitude *= 255.0 / edge_magnitude.max()

# 使用matplotlib显示原图和边缘检测结果
plt.figure(figsize=(10, 5))
plt.subplot(121), plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)), plt.title('Original Image')
plt.subplot(122), plt.imshow(edge_magnitude, cmap='gray'), plt.title('Edge Detection')
plt.show()

Prewitt梯度算子试验效果

图4

Sobel梯度算子边缘检测代码

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 读取图片
image = cv2.imread('aaa.jpg')

# 将图片转换为灰度图
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Sobel算子的参数
scale = 1
delta = 0
ddepth = cv2.CV_16S

# 计算x方向上的梯度
grad_x = cv2.Sobel(gray_image, ddepth, 1, 0, ksize=3, scale=scale, delta=delta, borderType=cv2.BORDER_DEFAULT)

# 计算y方向上的梯度
grad_y = cv2.Sobel(gray_image, ddepth, 0, 1, ksize=3, scale=scale, delta=delta, borderType=cv2.BORDER_DEFAULT)

# 计算两个方向的梯度的绝对值
abs_grad_x = cv2.convertScaleAbs(grad_x)
abs_grad_y = cv2.convertScaleAbs(grad_y)

# 合并梯度(近似)
edge_magnitude = cv2.addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0)

# 使用matplotlib显示原图和边缘检测结果
plt.figure(figsize=(10, 5))
plt.subplot(121), plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)), plt.title('Original Image')
plt.subplot(122), plt.imshow(edge_magnitude, cmap='gray'), plt.title('Edge Detection')
plt.show()

Sobel梯度算子试验效果

Sobel梯度算子试验效果