OpenCV系列第二集,图像特征检测。OpenCV被称作计算机的眼睛,是计算机视觉中最重要的部分。图像特征,其实就是指的图像的边缘、轮廓和棱角。如果想要绘制一张图像,首要做的就是绘制图像的轮廓,然后在轮廓的基础上绘制图像的线条(边缘)和角(角点),这样整张图像大体就展现出来了。
1. 边缘编辑与增强
图像的边缘通常指局部不连续的特征,即亮度变化最显著的区域。边缘检测在图像处理和计算机视觉任务中起着关键作用,有助于提取目标物体的轮廓信息,提高图像分析的准确性。
-
Canny 边缘检测简介
Canny 边缘检测是一种经典且广泛应用的边缘检测算法,由 John F. Canny 于 1986 年提出。它采用多阶段处理流程,包括 噪声去除、梯度计算、非极大值抑制 和 滞后阈值,以确保检测到清晰且可靠的边缘。
1. 噪声去除
由于边缘检测容易受到噪声影响,Canny 算法的第一步是对图像进行平滑处理,通常采用 高斯滤波(Gaussian Blur)。常见的滤波器尺寸为 5×5,通过卷积操作减少高频噪声,使边缘检测更稳定。
2. 计算图像梯度
图像梯度用于衡量像素灰度的变化情况。梯度的方向始终与边界垂直,可用于定位边缘。梯度计算通常使用 Sobel 算子,通过计算 x 方向(水平)和 y 方向(垂直)的导数,获得梯度幅值和方向:
\[G = \sqrt{G_x^2 + G_y^2}\\ \theta = \arctan\left(\frac{G_y}{G_x}\right)\]其中,$G_x$ 和 $G_y$ 分别表示水平方向和垂直方向的梯度。
在实际应用中,梯度方向通常被归为四类:水平、垂直、左上-右下对角线、右上-左下对角线。
3. 非极大值抑制
非极大值抑制(Non-Maximum Suppression, NMS)用于细化边缘,去除非边缘像素点。其核心思想是 仅保留局部梯度方向上最强的像素点,并抑制其他较弱的响应,以获得更精确的边缘信息。
具体方法是:
- 计算像素点梯度值,并根据梯度方向,与相邻的两个像素进行比较。
- 若该像素的梯度值 小于 其梯度方向上的邻居,则将其置为零(即非边缘)。
- 若该像素的梯度值 大于等于 其邻居,则保留。
4. 滞后阈值(Hysteresis Thresholding)
由于光照等因素,部分边界的梯度值较低。Canny 算法使用 双阈值 机制,即 高阈值(maxVal) 和 低阈值(minVal),来区分边缘像素:
- 若像素梯度值 高于 maxVal,则被认为是 强边缘,直接保留。
- 若像素梯度值 低于 minVal,则被认为是 非边缘,直接舍弃。
- 若梯度值 介于 minVal 和 maxVal 之间,则进一步判断该像素是否与某个强边缘像素相连:
- 若相连,则保留(弱边缘变为强边缘)。
- 若不相连,则舍弃。
通过这种方式,可以有效地滤除伪边缘,并保证边缘的连贯性和准确性。
实验
-
导入模块
import cv2 import matplotlib.pyplot as plt # 绘图文字使用黑体显示(显示中文,默认不支持中文) plt.rcParams['font.sans-serifil=['SimHei']
-
读取图像,将彩色图转换为灰度图
# 读取图像 img=cv2.imread( ‘img/0401.jpg') # 将彩色图转换为灰度图 img= cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
-
使用 Canny 边缘检测算法,配合滞后值处理图像
# 使用Canny边缘检测算法,将滞后闻值分别设定为200和300 edges1 = cv2.Canny(img, 200, 300)
-
编写可视化代码
plt.subplot(121) # 会制第一张子图,总共为1行2列 plt.title('原图') plt.imshow(img) # 去除图像的坐标尺 plt.xticks([]) plt.yticks([]) plt.subplot(122) # 给制第二张子图,总共为2行2列 plt.title('轮廓处理 1') plt.imshow(edges1, cmap='gray') plt.xticks([]) plt.yticks([]) # 显示图像效果 plt.show()
2. 图像轮廓检测
轮廓(Contour)是指由相邻像素点连接而成的曲线,这些像素点具有相同的颜色或灰度值。轮廓检测广泛应用于目标检测、物体分割和形状分析等计算机视觉任务。
1. 轮廓检测流程
轮廓检测通常需要以下步骤:
-
二值化处理
在查找轮廓之前,需要对图像进行 二值化(Thresholding)或 Canny 边缘检测,以减少图像复杂度,使轮廓更容易识别。
-
查找轮廓
OpenCV 提供的 cv2.findContours() 函数用于提取图像中的轮廓。需要注意的是,该函数会 修改原始图像,因此如果需要保留原图,应先创建副本进行处理。
-
轮廓的工作原理
在 OpenCV 中,轮廓检测类似于在黑色背景上寻找白色物体,因此建议在处理前确保前景为白色,背景为黑色。
2. 轮廓查找函数
在 OpenCV 中,cv2.findContours() 是常用的轮廓检测函数,其典型调用格式如下:
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
各参数说明:
thresh
:二值化后的图像,可以通过cv2.threshold()
或cv2.Canny()
生成。cv2.RETR_TREE
:轮廓检索模式,用于指定轮廓的层级关系:cv2.RETR_EXTERNAL
:仅检测最外层轮廓。cv2.RETR_LIST
:检测所有轮廓,但不建立层级关系。cv2.RETR_CCOMP
:检测所有轮廓,并将它们组织成两个层级(外轮廓和内洞)。cv2.RETR_TREE
:检测所有轮廓,并建立完整的层级结构。
cv2.CHAIN_APPROX_SIMPLE
:轮廓近似方式:cv2.CHAIN_APPROX_NONE
:存储轮廓上的所有点,不进行压缩。cv2.CHAIN_APPROX_SIMPLE
:仅存储直线段的端点,减少内存使用。例如,矩形轮廓仅存储四个角点,而非所有边上的像素点。
返回值:
contours
:包含所有轮廓的列表,每个轮廓由一系列坐标点表示。hierarchy
:包含轮廓的层级信息,可用于分析轮廓的嵌套关系。
3. 轮廓绘制函数
OpenCV 提供了 cv2.drawContours() 函数用于绘制检测到的轮廓。典型调用格式如下:
img = cv2.drawContours(img, contours, -1, (0, 0, 255), 2)
参数说明:
img
:原始图像,绘制轮廓的目标图像。contours
:轮廓列表,由cv2.findContours()
生成。1
:指定绘制的轮廓索引,1
表示绘制 所有轮廓。如果只想绘制特定轮廓,可以传入对应索引。(0, 0, 255)
:轮廓颜色,采用 BGR 格式,此处表示 红色。2
:线条宽度,表示轮廓线的像素宽度。
返回值:
img
:绘制轮廓后的图像,可用于可视化分析。
实验
-
导入模块 & 读取图像,并将其转换为灰度图
import cv2 img=cv2.imread('img/0402.jpg') img_gray=cv2.cvtColor(img, CV2.COLOR_BGR2GRAY)
-
采用二值化方式处理图像
# 采用二值化方式处理图像。像素值在182和255之间的数据为1,小于182的数据为0,ret, thresh=cv2.threshold(img_gray, 182, 255, 0)
-
查找轮廓
# 使用简易方式英取全部轮廓 Contours, hierarchy= cv2.findContours(thresh, cV2.RETR TREE, cV2.CHAIN_APPROX_SIMPLE)
-
绘制轮廓
# 传入的参数:图像、轮廓坐标、全部轮廓、轮廓颜色(红色),线宽 img = cv2.drawContours(img, contours, -1, (0,0,255), 2)
-
可视化图像
cv2.imshow('gray', img_gray) cv2.imshow(bin', thresh) cv2.imshow('contour', img) # 按任意键退出图像显示,结束程序 # 灰度图效果 # 二值化图效果 # 轮廓图效果 cv2.waitKey(0) cv2.destroyAllWindows()
小结
- 通过轮廓检测的应用,可以得到以下结论:
- 二值化取值不一定是按照灰度值 182 作为值切分的,可以根据每张图像的特点进行调整。
- 轮廓查找是二值化图像黑色和白色交界处的像素点位置。
3. 图像角点与线条检测
角点和线条检测是计算机视觉中的重要技术,用于特征提取、图像匹配和目标识别。角点通常对应于物体的拐角,例如 道路的交叉口、建筑物的顶点 等。在图像分析中,角点具有独特的局部特征,能够用于稳定地跟踪物体或进行几何变换估计。
1. 角点的定义
在计算机视觉领域,角点可以从两个角度定义:
- 角点是两个边缘的交点,例如十字路口或物体的棱角。
- 角点是邻域内具有两个主要方向的特征点,即当在不同方向移动窗口时,图像灰度值发生显著变化。
这些特性使角点在图像匹配、运动检测和三维重建等任务中尤为重要。
2. Harris 角点检测简介
Harris 角点检测 由 Chris Harris 和 Mike Stephens 在论文 “A Combined Corner and Edge Detector” 中提出,主要用于检测图像中的角点。
-
Harris 角点检测的基本原理
人眼识别角点的方式通常是在 一个局部窗口内 观察图像的灰度变化:
- 如果窗口在所有方向上移动,灰度值发生明显变化,则判定为角点。
- 如果窗口移动时,灰度值几乎不变,则窗口区域是平坦区域,不包含角点。
- 如果窗口仅在某个方向上变化,而在其他方向上不变,则该区域可能是一条直线。
Harris 角点检测基于 自相关矩阵(Second Moment Matrix),通过计算窗口内像素的梯度变化,判断该窗口是否包含角点。其核心公式为:
\[R = det(M) - k \cdot (trace(M))^2\]其中:
-
$M$ 是图像窗口内的 自相关矩阵:
\[M = \begin{bmatrix} I_x^2 & I_x I_y \\ I_x I_y & I_y^2 \end{bmatrix}\] - $I_x, I_y$ 是 Sobel 算子计算得到的 x 和 y 方向上的梯度。
- $det(M)$ 计算矩阵的行列式,衡量梯度的散布情况。
- $trace(M)$ 计算矩阵的迹,即特征值之和。
- $k$ 是经验系数,通常取 0.04 - 0.06。
当 $R$ 值较大时,像素点被认为是角点。
3. Harris 角点检测函数
OpenCV 提供了 cv2.cornerHarris() 进行角点检测,其典型调用格式如下:
dst = cv2.cornerHarris(gray, 2, 3, 0.04)
参数说明
gray
:输入的 灰度图像(必须是float32
类型)。2
:窗口大小,即检测角点时考虑的邻域范围。值越小,检测更加局部化;值过大会影响计算效率和准确性。3
:Sobel 算子计算梯度时的窗口大小,较小的值能获得更细节的梯度信息,较大的值会导致角点检测不稳定。0.04
:Harris 角点检测公式中的自由参数 ,通常设定在[0.04, 0.06]
之间。- 返回值
dst
:表示每个像素点的角点响应值,值越大,角点特征越明显。
实验
-
导入模块
import cv2 import numpy as np
-
图像预处理
# 读取图像 img=cv2.imread('img/0403.jpg') # 将图像转化成灰度图 gray=cv2.cvtColor(img, cV2.COLOR_BGR2GRAY) gray=np.float32(grey)
-
角点处理
# gray: 输入的float类型的灰度图 # 2:检测过程中考虑的领域大小 # 3:使用Scbel算法在求导时使用的窗口大小 # 0.04:Harris角点检测方程中的自由参数,取值范围为[0.04, 0.06] dst= cv2.cornerHarris(gray, 2, 3, 0.04) # 这里设定一个阈值,只要大于等于这个阈值就可认判定为角点 img[dst>0.01*dst.max()]=[0, 0, 255] #[0, 0, 255]为红色
-
可视化处理
cv2.imshow(‘dst’ ,img) if cv2.waitKey(0) & 0xff==27: cv2.destroyAllWindows()小结
小结
- 通过角点检测的使用,可以得到以下结论。
- 在查找角点之前,需要先将图像转换为 float 类型。
- 检测框和 Sobel 算法的计算窗口不能设置得太大,否则计算量会增加,并且检测效果会变差。