深度学习之opencv篇
引言:为什么选择 OpenCV?
在当今数字化时代,图像处理和计算机视觉技术已渗透到我们生活的方方面面 —— 从手机拍照的美颜功能到自动驾驶的环境感知,从医学影像分析到安防监控系统,背后都离不开高效的视觉处理算法。而OpenCV(Open Source Computer Vision Library) 作为这一领域最受欢迎的开源库,自 2000 年首次发布以来,已成为百万开发者的首选工具。
OpenCV 由英特尔公司发起并主导开发,如今由非盈利组织 OpenCV.org 维护。它支持多种编程语言(C++、Python、Java 等),可在 Windows、Linux、macOS、Android、iOS 等多平台运行,更重要的是,它提供了超过 2500 个优化过的算法,涵盖从基础图像处理到高级计算机视觉的全领域应用。
本文将带您全面探索 OpenCV 的核心功能,通过大量实战代码示例,从基础操作到高级应用,逐步掌握这一强大工具的使用技巧。无论您是图像处理新手,还是希望提升技能的开发者,本文都能为您提供系统的指导。
一、OpenCV 环境搭建
在开始之前,我们需要先搭建 OpenCV 开发环境。本节将详细介绍不同操作系统下的安装方法,以 Python 为例(因其简洁易学,适合快速验证算法)。
1.1 Windows 系统安装
- 安装 Python:前往 Python 官网 下载最新版本,勾选 "Add Python to PATH",按提示完成安装。
- 安装 OpenCV:打开命令提示符(CMD),输入以下命令:
bash
pip install opencv-python # 基础包 pip install opencv-contrib-python # 扩展包(包含专利算法)
- 验证安装:
python
运行
import cv2 print(cv2.__version__) # 输出版本号即表示安装成功
1.2 Linux/macOS 系统安装
- 安装 Python:
- Linux:多数系统预装 Python,可通过
sudo apt install python3
安装 - macOS:使用 Homebrew:
brew install python3
- Linux:多数系统预装 Python,可通过
- 安装 OpenCV:
bash
pip3 install opencv-python pip3 install opencv-contrib-python
- 验证安装:同 Windows 步骤。
二、OpenCV 核心概念与基础操作
2.1 图像的表示方式
在计算机中,图像本质上是由像素点组成的矩阵:
- 灰度图:单通道矩阵,每个像素值范围为 [0, 255](0 表示黑色,255 表示白色)
- 彩色图:多通道矩阵,OpenCV 默认使用 BGR 格式(与 RGB 相反),每个通道像素值范围同样为 [0, 255]
例如,一张 480×640 的彩色图在 OpenCV 中会被表示为一个形状为 (480, 640, 3) 的 NumPy 数组(行 × 列 × 通道)。
2.2 图像的读取、显示与保存
这是 OpenCV 最基础的操作,也是所有后续处理的起点。
python
运行
import cv2
import numpy as np# 1. 读取图像
# 参数1:图像路径(中文路径需注意编码问题)
# 参数2:读取模式(cv2.IMREAD_COLOR:彩色图,默认值;cv2.IMREAD_GRAYSCALE:灰度图;cv2.IMREAD_UNCHANGED:包含Alpha通道)
img = cv2.imread('test.jpg', cv2.IMREAD_COLOR)# 检查图像是否读取成功
if img is None:print("无法读取图像,请检查路径是否正确")
else:# 2. 显示图像# 参数1:窗口名称(字符串)# 参数2:要显示的图像cv2.imshow('Original Image', img)# 等待用户按键(0表示无限等待,单位为毫秒)cv2.waitKey(0)# 3. 保存图像# 参数1:保存路径及文件名# 参数2:要保存的图像cv2.imwrite('saved_image.jpg', img)# 关闭所有OpenCV窗口cv2.destroyAllWindows()
注意事项:
- 若图像路径包含中文,直接使用
cv2.imread
可能失败,需通过 NumPy 读取:python
运行
import numpy as np img = cv2.imdecode(np.fromfile('中文路径.jpg', dtype=np.uint8), cv2.IMREAD_COLOR)
cv2.waitKey()
是必不可少的,否则窗口会一闪而过。
2.3 图像属性与通道操作
获取图像的基本信息(尺寸、通道数等),并对通道进行拆分与合并:
python
运行
import cv2img = cv2.imread('test.jpg')# 获取图像属性
height, width, channels = img.shape # 彩色图
# 灰度图的shape为(height, width),可通过len(img.shape)判断:1表示灰度图,3表示彩色图
print(f"图像尺寸:{height}×{width},通道数:{channels}")
print(f"数据类型:{img.dtype}") # 通常为uint8# 拆分通道
b, g, r = cv2.split(img) # 拆分B、G、R通道(注意顺序)# 合并通道
merged_img = cv2.merge([b, g, r]) # 需按BGR顺序合并# 单独显示某个通道(以蓝色通道为例)
# 方法1:将其他通道设为0
blue_img = np.zeros_like(img)
blue_img[:, :, 0] = b # 仅保留蓝色通道# 方法2:创建单通道图像后扩展为3通道(便于显示)
blue_gray = cv2.cvtColor(blue_img, cv2.COLOR_BGR2GRAY)
blue_3ch = cv2.cvtColor(blue_gray, cv2.COLOR_GRAY2BGR)# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Blue Channel', blue_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
扩展知识:通道拆分也可通过数组切片实现(效率更高):
python
运行
b = img[:, :, 0]
g = img[:, :, 1]
r = img[:, :, 2]
三、图像几何变换
图像几何变换是调整图像尺寸、角度或位置的基础操作,广泛应用于图像对齐、缩放适配等场景。
3.1 图像缩放
python
运行
import cv2img = cv2.imread('test.jpg')# 方法1:指定目标尺寸(width, height)
resized1 = cv2.resize(img, (600, 400))# 方法2:指定缩放比例
scale = 0.5 # 缩小为原来的50%
resized2 = cv2.resize(img, None, fx=scale, fy=scale)# 选择插值方法(不同场景适用不同方法)
# - cv2.INTER_NEAREST:最近邻插值(速度快,质量低)
# - cv2.INTER_LINEAR:双线性插值(默认值,平衡速度和质量)
# - cv2.INTER_CUBIC:双三次插值(质量高,速度慢,适合放大图像)
# - cv2.INTER_AREA:区域插值(适合缩小图像,可避免摩尔纹)
resized_high_quality = cv2.resize(img, (1200, 800), interpolation=cv2.INTER_CUBIC)# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Resized (600x400)', resized1)
cv2.imshow('Resized (50%)', resized2)
cv2.waitKey(0)
cv2.destroyAllWindows()
3.2 图像旋转
python
运行
import cv2
import numpy as npimg = cv2.imread('test.jpg')
height, width = img.shape[:2]# 1. 获取旋转矩阵
# 参数1:旋转中心(这里设为图像中心)
# 参数2:旋转角度(正值表示逆时针旋转)
# 参数3:缩放比例
M = cv2.getRotationMatrix2D((width/2, height/2), 45, 1)# 2. 执行旋转
# 参数3:输出图像尺寸(width, height)
rotated = cv2.warpAffine(img, M, (width, height))# 扩展:旋转后避免裁剪(计算新尺寸)
angle = 45
radians = np.radians(angle)
new_width = int(width * np.abs(np.cos(radians)) + height * np.abs(np.sin(radians)))
new_height = int(width * np.abs(np.sin(radians)) + height * np.abs(np.cos(radians)))# 调整旋转矩阵(使旋转中心适配新尺寸)
M[0, 2] += (new_width / 2) - (width / 2)
M[1, 2] += (new_height / 2) - (height / 2)rotated_full = cv2.warpAffine(img, M, (new_width, new_height))# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Rotated (45 degrees)', rotated)
cv2.imshow('Rotated (no crop)', rotated_full)
cv2.waitKey(0)
cv2.destroyAllWindows()
3.3 图像平移与翻转
python
运行
import cv2
import numpy as npimg = cv2.imread('test.jpg')
height, width = img.shape[:2]# 1. 图像平移
# 平移矩阵:[[1,0,dx], [0,1,dy]],dx为水平偏移(正值右移),dy为垂直偏移(正值下移)
dx, dy = 100, 50
M = np.float32([[1, 0, dx], [0, 1, dy]])
translated = cv2.warpAffine(img, M, (width, height))# 2. 图像翻转
# 参数2:翻转方式(0:垂直翻转;1:水平翻转;-1:同时垂直和水平翻转)
flip_vertical = cv2.flip(img, 0)
flip_horizontal = cv2.flip(img, 1)
flip_both = cv2.flip(img, -1)# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Translated', translated)
cv2.imshow('Flip Vertical', flip_vertical)
cv2.imshow('Flip Horizontal', flip_horizontal)
cv2.imshow('Flip Both', flip_both)
cv2.waitKey(0)
cv2.destroyAllWindows()
四、色彩空间转换
色彩空间是描述颜色的数学模型,不同场景需要不同的色彩空间。OpenCV 支持多种色彩空间转换,以下是最常用的几种。
4.1 常用色彩空间简介
色彩空间 | 特点与应用场景 |
---|---|
BGR | OpenCV 默认格式,适合显示和存储 |
RGB | 多数图像库(如 PIL)使用的格式,与 BGR 仅通道顺序不同 |
GRAY | 灰度图,单通道,简化计算,常用于预处理 |
HSV | 分离亮度(V)和色彩(H、S),适合颜色检测 |
YCrCb | 用于 JPEG 压缩,Y 通道为亮度,适合肤色检测 |
4.2 色彩空间转换代码示例
python
运行
import cv2img = cv2.imread('test.jpg')# 1. BGR 转 RGB(用于与其他库交互)
rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# 2. BGR 转灰度图
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 3. BGR 转 HSV
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)# 4. BGR 转 YCrCb
ycrcb_img = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)# 显示结果
cv2.imshow('BGR', img)
cv2.imshow('RGB', rgb_img) # 注意:OpenCV显示RGB图会偏色,因其默认按BGR解析
cv2.imshow('GRAY', gray_img)
cv2.imshow('HSV', hsv_img)
cv2.imshow('YCrCb', ycrcb_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
4.3 实战:基于 HSV 的颜色检测
HSV 色彩空间对光照变化不敏感,是颜色检测的理想选择。以下示例实现检测图像中的红色物体:
python
运行
import cv2
import numpy as npimg = cv2.imread('red_object.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)# 定义红色的HSV范围(红色在HSV中有两个区间)
lower_red1 = np.array([0, 120, 70])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([170, 120, 70])
upper_red2 = np.array([180, 255, 255])# 创建掩码(符合范围的像素设为255,否则为0)
mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
mask = mask1 | mask2 # 合并两个掩码# 对原图和掩码执行位运算,提取红色区域
result = cv2.bitwise_and(img, img, mask=mask)# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Mask', mask)
cv2.imshow('Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
技巧:如何确定颜色的 HSV 范围?
- 用绘图工具取目标颜色的 BGR 值
- 转换为 HSV 后,上下浮动一定范围作为阈值:
python
运行
bgr_color = np.uint8([[[0, 0, 255]]]) # 红色的BGR值 hsv_color = cv2.cvtColor(bgr_color, cv2.COLOR_BGR2HSV) print(hsv_color) # 输出HSV值,以此为中心设置范围
五、图像阈值处理
阈值处理是将灰度图转换为二值图的过程,通过设定阈值将像素分为两类(黑 / 白),常用于图像分割和特征提取。
5.1 简单阈值处理
python
运行
import cv2
import numpy as np# 读取灰度图
img = cv2.imread('text_image.jpg', cv2.IMREAD_GRAYSCALE)# 简单阈值处理
# 参数1:输入图像(必须为单通道)
# 参数2:阈值
# 参数3:最大值(超过阈值的像素设为此值)
# 参数4:阈值类型
ret, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) # 超过阈值为最大值,否则为0
ret, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV) # 与THRESH_BINARY相反
ret, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC) # 超过阈值的像素设为阈值,否则不变
ret, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO) # 超过阈值的像素不变,否则为0
ret, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV) # 与THRESH_TOZERO相反# 显示结果
titles = ['Original', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]for i in range(6):cv2.imshow(titles[i], images[i])cv2.waitKey(0)
cv2.destroyAllWindows()
5.2 自适应阈值处理
简单阈值使用全局阈值,当图像光照不均匀时效果较差。自适应阈值会根据像素周围的区域动态调整阈值:
python
运行
import cv2img = cv2.imread('text_image.jpg', cv2.IMREAD_GRAYSCALE)
# 去除噪声(自适应阈值对噪声敏感)
img = cv2.GaussianBlur(img, (5, 5), 0)# 简单阈值(对比用)
ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)# 自适应阈值
# 参数2:最大值
# 参数3:自适应方法(ADAPTIVE_THRESH_MEAN_C:均值;ADAPTIVE_THRESH_GAUSSIAN_C:高斯加权均值)
# 参数4:阈值类型
# 参数5:块大小(计算阈值的区域大小,必须为奇数)
# 参数6:常数(从均值/加权均值中减去的值)
th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
th3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2)# 显示结果
titles = ['Original', 'Global Threshold (127)', 'Adaptive Mean', 'Adaptive Gaussian']
images = [img, th1, th2, th3]for i in range(4):cv2.imshow(titles[i], images[i])cv2.waitKey(0)
cv2.destroyAllWindows()
5.3 Otsu 阈值法
Otsu 算法会自动计算最优阈值,适用于双峰直方图的图像(像素值集中在两个区域):
python
运行
import cv2
import matplotlib.pyplot as pltimg = cv2.imread('noisy_image.jpg', cv2.IMREAD_GRAYSCALE)# 简单阈值(手动设置阈值)
ret1, th1 = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY)# Otsu 阈值(自动计算阈值)
ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)# 先高斯滤波再Otsu阈值(处理噪声)
blur = cv2.GaussianBlur(img, (5, 5), 0)
ret3, th3 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)# 绘制直方图对比
images = [img, 0, th1,img, 0, th2,blur, 0, th3]
titles = ['Original', 'Histogram', 'Global (v=100)','Original', 'Histogram', "Otsu's (v={})".format(ret2),'Gaussian Blur', 'Histogram', "Otsu's (v={})".format(ret3)]for i in range(3):plt.subplot(3, 3, i*3+1), plt.imshow(images[i*3], 'gray')plt.title(titles[i*3]), plt.xticks([]), plt.yticks([])plt.subplot(3, 3, i*3+2), plt.hist(images[i*3].ravel(), 256)plt.title(titles[i*3+1]), plt.xticks([]), plt.yticks([])plt.subplot(3, 3, i*3+3), plt.imshow(images[i*3+2], 'gray')plt.title(titles[i*3+2]), plt.xticks([]), plt.yticks([])plt.show()
六、图像滤波与平滑
图像滤波用于去除噪声或提取特征,是图像处理中的关键步骤。OpenCV 提供了多种滤波方法,适用于不同类型的噪声。
6.1 均值滤波
均值滤波将每个像素替换为其邻域内像素的平均值,适用于去除高斯噪声,但会使图像模糊。
python
运行
import cv2img = cv2.imread('noisy_image.jpg')# 均值滤波
# 参数:卷积核大小(必须为奇数)
blur = cv2.blur(img, (5, 5))# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Mean Blur', blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
6.2 高斯滤波
高斯滤波使用高斯核进行加权平均,相比均值滤波,对边缘的模糊程度更低。
python
运行
import cv2img = cv2.imread('noisy_image.jpg')# 高斯滤波
# 参数1:卷积核大小(必须为奇数)
# 参数2:x方向标准差(0表示自动计算)
# 参数3:y方向标准差(0表示自动计算)
gaussian_blur = cv2.GaussianBlur(img, (5, 5), 0)# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Gaussian Blur', gaussian_blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
6.3 中值滤波
中值滤波将每个像素替换为其邻域内像素的中值,特别适合去除椒盐噪声(随机出现的黑白点)。
python
运行
import cv2img = cv2.imread('salt_pepper_noise.jpg')# 中值滤波
# 参数:卷积核大小(必须为奇数)
median_blur = cv2.medianBlur(img, 5)# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Median Blur', median_blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
6.4 双边滤波
双边滤波在平滑图像的同时保留边缘,结合了空间高斯权重和灰度值相似性权重。
python
运行
import cv2img = cv2.imread('texture_image.jpg')# 双边滤波
# 参数1:直径(像素邻域大小)
# 参数2:颜色空间标准差(越大,允许更多颜色参与滤波)
# 参数3:坐标空间标准差(越大,更远的像素也会影响结果)
bilateral = cv2.bilateralFilter(img, 9, 75, 75)# 高斯滤波对比(边缘会模糊)
gaussian = cv2.GaussianBlur(img, (9, 9), 0)# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Bilateral Filter', bilateral)
cv2.imshow('Gaussian Blur', gaussian)
cv2.waitKey(0)
cv2.destroyAllWindows()
七、边缘检测
边缘是图像中灰度变化剧烈的区域,包含了物体的重要特征。边缘检测是目标识别、图像分割的基础。
7.1 Canny 边缘检测
Canny 边缘检测是一种多阶段算法,具有良好的抗噪声能力和边缘定位精度,是最常用的边缘检测方法。
python
运行
import cv2img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)# 步骤1:降噪(Canny对噪声敏感)
blur = cv2.GaussianBlur(img, (3, 3), 0)# 步骤2:Canny边缘检测
# 参数1:输入图像
# 参数2:低阈值
# 参数3:高阈值(通常为低阈值的2-3倍)
# 参数4:Sobel算子大小(默认3)
# 参数5:L2梯度(True表示使用L2范数,False表示使用L1范数)
edges = cv2.Canny(blur, 50, 150, apertureSize=3, L2gradient=True)# 显示结果
cv2.imshow('Original', img)
cv2.imshow('Canny Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
Canny 算法原理:
- 高斯滤波去除噪声
- 计算梯度强度和方向(使用 Sobel 算子)
- 非极大值抑制(保留局部最大值,细化边缘)
- 双阈值检测(确定强边缘和弱边缘)
- 边缘连接(弱边缘若与强边缘相连则保留)
7.2 Sobel 算子与 Laplacian 算子
python
运行
import cv2
import numpy as npimg = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)
img = cv2.GaussianBlur(img, (3, 3), 0) # 降噪# Sobel算子(计算x和y方向的梯度)
# 参数2:输出图像深度(-1表示与输入相同)
# 参数3:x方向导数阶数
# 参数4:y方向导数阶数
# 参数5:算子大小
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)# 转换为uint8(Sobel可能输出负值,需取绝对值)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.convertScaleAbs(sobely)# 合并x和y方向梯度
sobel_combined = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)# Laplacian算子(二阶导数,对噪声更敏感)
laplacian = cv2.Laplacian(img, cv2.CV_64F, ksize=3)
laplacian = cv2.convertScaleAbs(laplacian)# 显示结果
titles = ['Original', 'Sobel X', 'Sobel Y', 'Sobel Combined', 'Laplacian']
images = [img, sobelx, sobely, sobel_combined, laplacian]for i in range(5):cv2.imshow(titles[i], images[i])cv2.waitKey(0)
cv2.destroyAllWindows()
八、特征检测与描述
特征是图像中具有独特性和稳定性的区域(如角点、边缘、斑点等),特征检测与描述是目标匹配、图像拼接等高级应用的基础。
8.1 Harris 角点检测
角点是图像中两个边缘的交点,Harris 算法通过检测灰度变化剧烈的区域来识别角点。
python
运行
import cv2
import numpy as npimg = cv2.imread('chessboard.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = np.float32(gray)# Harris角点检测
# 参数1:输入图像(单通道,float32)
# 参数2:角点检测的邻域大小
# 参数3:Sobel算子孔径大小
# 参数4:Harris检测自由参数
dst = cv2.cornerHarris(gray, 2, 3, 0.04)# 膨胀角点(便于显示)
dst = cv2.dilate(dst, None)# 标记角点(阈值为最大值的0.01)
img[dst > 0.01 * dst.max()] = [0, 0, 255] # 红色标记cv2.imshow('Harris Corners', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
8.2 SIFT 特征检测(尺度不变特征变换)
SIFT 特征具有尺度不变性和旋转不变性,即使图像缩放、旋转后仍能稳定识别。
python
运行
import cv2# 注意:SIFT算法受专利保护,需安装opencv-contrib-python
img = cv2.imread('lena.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 创建SIFT对象
sift = cv2.SIFT_create()# 检测关键点并计算描述符
kp, des = sift.detectAndCompute(gray, None)# 在图像上绘制关键点
# 参数3:标记点的颜色(BGR)
# 参数4:标记点的大小
img_with_kp = cv2.drawKeypoints(img, kp, None, color=(0, 255, 0), flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)cv2.imshow('SIFT Keypoints', img_with_kp)
cv2.waitKey(0)
cv2.destroyAllWindows()
8.3 ORB 特征检测(替代 SIFT 的免费方案)
ORB 是 SIFT 和 SURF 的替代方案,具有更快的速度,且不受专利限制。
python
运行
import cv2img = cv2.imread('lena.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 创建ORB对象
orb = cv2.ORB_create()# 检测关键点并计算描述符
kp, des = orb.detectAndCompute(gray, None)# 绘制关键点
img_with_kp = cv2.drawKeypoints(img, kp, None, color=(0, 255, 0), flags=0)cv2.imshow('ORB Keypoints', img_with_kp)
cv2.waitKey(0)
cv2.destroyAllWindows()
8.4 特征匹配
使用 ORB 特征实现两幅图像的匹配:
python
运行
import cv2
import numpy as np# 读取两幅图像
img1 = cv2.imread('object.jpg', 0) # 目标图像
img2 = cv2.imread('scene.jpg', 0) # 场景图像# 初始化ORB
orb = cv2.ORB_create()# 检测特征点并计算描述符
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)# 创建暴力匹配器
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)# 匹配描述符
matches = bf.match(des1, des2)# 按距离排序(保留最佳匹配)
matches = sorted(matches, key=lambda x: x.distance)# 绘制前10个匹配点
img_matches = cv2.drawMatches(img1, kp1, img2, kp2, matches[:10], None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)cv2.imshow('Matches', img_matches)
cv2.waitKey(0)
cv2.destroyAllWindows()
九、视频处理基础
OpenCV 不仅能处理图像,还能读取、处理和保存视频,支持从文件或摄像头获取视频流。
9.1 读取与显示视频文件
python
运行
import cv2# 打开视频文件(参数也可以是摄像头索引,0表示默认摄像头)
cap = cv2.VideoCapture('test_video.mp4')# 检查视频是否成功打开
if not cap.isOpened():print("无法打开视频文件")exit()# 循环读取视频帧
while cap.isOpened():# 读取一帧(ret为布尔值,表示是否成功读取;frame为帧图像)ret, frame = cap.read()if not ret:print("已到达视频末尾")break# 显示帧(可在此处添加帧处理代码)cv2.imshow('Video Frame', frame)# 按下 'q' 键退出if cv2.waitKey(25) & 0xFF == ord('q'):break# 释放资源
cap.release()
cv2.destroyAllWindows()
9.2 视频属性与控制
python
运行
import cv2cap = cv2.VideoCapture('test_video.mp4')# 获取视频属性
fps = cap.get(cv2.CAP_PROP_FPS) # 帧率
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # 宽度
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 高度
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) # 总帧数
duration = frame_count / fps # 视频时长(秒)print(f"帧率:{fps:.2f}")
print(f"尺寸:{width}×{height}")
print(f"总帧数:{frame_count}")
print(f"时长:{duration:.2f}秒")# 设置视频属性(并非所有属性都可修改)
cap.set(cv2.CAP_PROP_POS_FRAMES, 100) # 跳转到第100帧# 读取并显示指定帧
ret, frame = cap.read()
if ret:cv2.imshow('Frame 100', frame)cv2.waitKey(0)cap.release()
cv2.destroyAllWindows()
9.3 保存视频
python
运行
import cv2cap = cv2.VideoCapture(0) # 打开摄像头# 定义编码器(不同平台支持的编码器不同)
# Windows:DIVX;Linux:XVID;macOS:avc1
fourcc = cv2.VideoWriter_fourcc(*'XVID')# 创建VideoWriter对象
# 参数1:输出文件名
# 参数2:编码器
# 参数3:帧率
# 参数4:视频尺寸
out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480))while cap.isOpened():ret, frame = cap.read()if not ret:break# 可在此处添加帧处理(如灰度化、边缘检测等)# frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)# frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR) # 保存灰度视频需转回3通道# 写入帧out.write(frame)cv2.imshow('Frame', frame)if cv2.waitKey(1) & 0xFF == ord('q'):break# 释放资源
cap.release()
out.release()
cv2.destroyAllWindows()
十、背景减除与运动检测
背景减除是从视频中提取运动物体的常用技术,通过构建背景模型并与当前帧对比,检测出前景(运动物体)。
python
运行
import cv2
import numpy as np# 打开摄像头或视频文件
cap = cv2.VideoCapture(0)# 创建背景减除器(三种常用算法)
# 1. MOG2:基于高斯混合模型
fgbg = cv2.createBackgroundSubtractorMOG2(history=500, detectShadows=True)
# 2. KNN:基于K近邻
# fgbg = cv2.createBackgroundSubtractorKNN(history=500, detectShadows=True)
# 3. GMG:结合静态背景估计和贝叶斯分割(对噪声敏感,需预处理)
# kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
# fgbg = cv2.createBackgroundSubtractorGMG()while True:ret, frame = cap.read()if not ret:break# 应用背景减除fgmask = fgbg.apply(frame)# 对MOG2和KNN,阴影会被标记为灰色(值为127),可将其设为0if isinstance(fgbg, cv2.BackgroundSubtractorMOG2) or isinstance(fgbg, cv2.BackgroundSubtractorKNN):fgmask[fgmask == 127] = 0# 形态学操作去除噪声kernel = np.ones((3, 3), np.uint8)fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)# 找到轮廓(检测运动物体)contours, _ = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 绘制轮廓for c in contours:# 过滤小轮廓(排除噪声)if cv2.contourArea(c) < 500:continuex, y, w, h = cv2.boundingRect(c)cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)# 显示结果cv2.imshow('Frame', frame)cv2.imshow('FG Mask', fgmask)if cv2.waitKey(30) & 0xFF == ord('q'):breakcap.release()
cv2.destroyAllWindows()
十一、综合案例:人脸检测与识别
结合上述知识,我们实现一个基于 Haar 级联分类器的人脸检测系统。
11.1 人脸检测基础
python
运行
import cv2# 加载Haar级联分类器(OpenCV自带)
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')# 读取图像并转换为灰度图
img = cv2.imread('people.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 检测人脸
# 参数1:灰度图
# 参数2:缩放因子(1.1表示每次缩小10%)
# 参数3:最小邻居数(越高检测越严格)
faces = face_cascade.detectMultiScale(gray, 1.1, 4)# 在人脸周围绘制矩形,并检测眼睛
for (x, y, w, h) in faces:cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)# 人脸区域roi_gray = gray[y:y+h, x:x+w]roi_color = img[y:y+h, x:x+w]# 在人脸区域检测眼睛eyes = eye_cascade.detectMultiScale(roi_gray)for (ex, ey, ew, eh) in eyes:cv2.rectangle(roi_color, (ex, ey), (ex+ew, ey+eh), (0, 255, 0), 2)cv2.imshow('Face Detection', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
11.2 实时人脸检测(摄像头)
python
运行
import cv2face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')cap = cv2.VideoCapture(0)while True:ret, frame = cap.read()if not ret:breakgray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)faces = face_cascade.detectMultiScale(gray, 1.1, 4)for (x, y, w, h) in faces:cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)# 添加文字标签cv2.putText(frame, 'Face', (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)cv2.imshow('Real-time Face Detection', frame)if cv2.waitKey(1) & 0xFF == ord('q'):breakcap.release()
cv2.destroyAllWindows()
结语:OpenCV 的进阶之路
本文涵盖了 OpenCV 的核心功能,从基础的图像读写到高级的特征检测和视频处理,通过大量代码示例帮助您快速上手。但 OpenCV 的能力远不止于此,以下是值得深入学习的进阶方向:
- 深度学习与 OpenCV:OpenCV 支持加载 TensorFlow、PyTorch 等框架训练的模型,实现更精准的目标检测(如 YOLO、SSD)、人脸识别等。
- 3D 计算机视觉:利用 OpenCV 处理立体图像,计算深度信息,实现三维重建。
- 性能优化:通过 OpenCV 的 GPU 加速模块(cv2.cuda)提升处理速度,满足实时应用需求。
- 行业应用:结合具体领域(如医学影像、自动驾驶、机器人视觉)深入研究解决方案。
OpenCV 官网(https://opencv.org/)和官方文档是学习的最佳资源,建议结合实际项目实践,不断提升技能。
希望本文能成为您探索计算机视觉世界的起点,祝您在 OpenCV 的学习之路上取得进步!