OpenCV 核心概念
python import cv2 img = cv2.imread('example.jpg') print(img.shape) # (高, 宽, 通道数) print(img.dtype) # 数据类型,例如 uint8
了解 `shape` 和 `dtype` 是编写健壮图像处理代码的第一步。
## 2. 像素与颜色空间
### 2.1 像素值的含义
在 OpenCV 中,默认读取的图像是 **BGR 格式**(蓝、绿、红),而非更常见的 RGB。这是一点重要的历史遗留差异,操作时需特别留意。
- 对于 8 位图像,B、G、R 三个通道的取值范围均为 0 - 255。
- 对于灰度图像,只有一个通道,直接表示亮度。
### 2.2 常用颜色空间转换
颜色空间是组织和描述色彩的方式。除了 BGR,以下三种空间在计算机视觉任务中非常实用:
- **灰度 (GRAY)**:将彩色图转为单通道亮度图,常用于边缘检测、阈值处理等。
- **HSV**:色相(H)、饱和度(S)、明度(V)。适合用于颜色分割(例如提取某一特定颜色的物体)。
- **Lab**:更接近人眼感知,L 通道表示亮度,a 和 b 表示颜色对立维度。
转换方法:
```python
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
熟练掌握颜色空间转换是进行复杂视觉任务的基础。
3. 图像的基本 I/O:读取、显示与保存
这是每个 OpenCV 程序的起点和终点。
3.1 读取图像
img = cv2.imread('path/to/image.jpg')
如果路径错误,img 为 None。实际项目中务必检查返回值。
3.2 显示图像
cv2.imshow('Window Title', img)
cv2.waitKey(0) # 等待按键,0 表示无限等待
cv2.destroyAllWindows()
3.3 保存图像
cv2.imwrite('output.png', img)
imwrite 会根据文件扩展名自动选择压缩格式。
4. 区域操作与 ROI(感兴趣区域)
很多任务不需要处理整幅图像,而只关心某个局部。OpenCV 通过切片(numpy 数组操作)直接提取 ROI。
# 假设 img 是一张 BGR 图像
roi = img[100:300, 200:400] # 提取行 100~300,列 200~400 的区域
对 ROI 的修改会直接反映到原图上,因为它们共享同一块数据内存。如果想要独立副本,需使用 .copy() 方法。
ROI 常用于人脸检测、目标跟踪等场景中的局部处理。
5. 基本图像操作
5.1 算术运算与亮度调节
图像之间可以直接使用 + 和 - 运算符,但更好的方式是使用 cv2.add() 和 cv2.subtract(),因为它们会自动进行饱和运算(超过 255 时截断为 255,低于 0 时截断为 0,防止溢出)。
bright = cv2.add(img, 50) # 整体提亮
dark = cv2.subtract(img, 50) # 整体变暗
5.2 图像混合
将两幅图像按权重叠加:
blend = cv2.addWeighted(img1, 0.7, img2, 0.3, 0)
5.3 按位操作
cv2.bitwise_and()、cv2.bitwise_or() 等操作通常与掩膜(mask)配合使用,实现图像的局部替换或提取。
6. 阈值处理:图像的二值化
阈值(Threshold)是将灰度图转换为二值图的关键操作,广泛用于物体分割和轮廓检测。
6.1 简单阈值
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
所有灰度值大于 127 的像素设为 255(白色),其余设为 0(黑色)。
6.2 自适应阈值与 Otsu
- 自适应阈值 适合光照不均匀的图像,阈值由像素周围区域计算得出。
- Otsu 阈值 自动计算最佳全局阈值,适合前景与背景有明显双峰直方图的图像。
th_otsu = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
7. 图像平滑与滤波
在消除噪声、提取轮廓前,常常需要对图像进行平滑处理。
7.1 均值滤波
用一块区域内的像素平均值替换中心像素,速度快但会模糊边缘。
blur = cv2.blur(img, (5,5))
7.2 高斯滤波
权重按照高斯分布分配,中心像素权重更高,去噪的同时更好地保留边缘信息。
gauss = cv2.GaussianBlur(img, (5,5), 0)
7.3 中值滤波
用区域内像素中值替换中心值,对椒盐噪声效果显著。
median = cv2.medianBlur(img, 5)
8. 边缘检测:Canny 算法
边缘是指图像中亮度变化剧烈的区域。Canny 边缘检测是一种经典的多级算法,使用方便且效果稳定。
edges = cv2.Canny(image, threshold1, threshold2)
threshold1和threshold2是两个滞后阈值,边缘梯度高于高阈值时确定为边缘,低于低阈值时舍弃,介于两者之间的则根据连接情况判断。
通常在执行 Canny 之前,会先用高斯滤波平滑图像,以减少噪声产生的伪边缘。
9. 轮廓:形状分析与目标检测的基石
轮廓是一系列连续的边缘点,可以表示物体的外边界。
9.1 查找轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
thresh是一幅二值图像(通常来自阈值或边缘检测输出)。- 返回的
contours是一个列表,每个元素是一组点坐标。
9.2 绘制轮廓
cv2.drawContours(img, contours, -1, (0,255,0), 3)
9.3 轮廓特征提取
对于每个轮廓,可以计算面积、周长、外接矩形、质心等:
area = cv2.contourArea(cnt)
x, y, w, h = cv2.boundingRect(cnt)
这些特征是进行形状识别、物件计数的核心手段。
10. 视频处理入门
OpenCV 同样可以处理视频流(来自文件或摄像头),其核心概念与图像处理完全一致——视频本质上是一帧一帧的图像序列。
10.1 捕捉视频
cap = cv2.VideoCapture(0) # 0 表示默认摄像头
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# 在此对 frame 进行图像处理
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()