第2章 OpenCV基础操作:图像读取与显示、视频读取与保存、图像色彩空间转换、图像几何变换、图像滤波与边缘检测

各位同学,欢迎来到第二章。这一章咱们要打的地基,是OpenCV最核心的几项基本功。说白了,就是怎么把一张图、一段视频请进内存,怎么折腾它,再把它送出去。

我刚开始学OpenCV那会儿,觉得这些操作太基础了,不就是读个图嘛。后来做项目才发现,很多坑就埋在这些基础操作里。比如色彩空间搞错了,检测结果全乱套;滤波参数没调好,边缘检测出来一堆噪点。嗯,咱们今天就把这些基础夯实了。

本章核心能力:掌握OpenCV的I/O操作与图像预处理流水线,为后续YOLO目标检测打下坚实基础。

OpenCV基础操作知识体系 图像读取与显示 视频读取与保存 色彩空间转换 图像几何变换 滤波与边缘检测 每个模块都是后续YOLO检测流水线的必备环节,缺一不可

2.1 图像读取与显示

先来最基础的。OpenCV用imread()读图,用imshow()显示。听起来简单,但参数选不对,后面全白费。

import cv2

# 读取图像 - 彩色模式
img = cv2.imread('cat.jpg', cv2.IMREAD_COLOR)

# 读取图像 - 灰度模式
img_gray = cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)

# 显示图像
cv2.imshow('My Image', img)
cv2.waitKey(0)  # 等待按键
cv2.destroyAllWindows()

我曾经踩过的坑:imread()默认返回BGR格式,不是RGB!如果你直接用matplotlib显示,颜色会偏蓝偏黄。我有个项目因此排查了整整一下午,最后发现是色彩通道顺序搞反了。

读取模式 标志 说明
彩色 cv2.IMREAD_COLOR 默认值,返回3通道BGR图像
灰度 cv2.IMREAD_GRAYSCALE 单通道,8位深度
不变 cv2.IMREAD_UNCHANGED 包含Alpha通道(如果有)

个人习惯:我一般用cv2.IMREAD_UNCHANGED读图,保留所有通道信息。后面需要什么再转,避免信息丢失。

2.2 视频读取与保存

视频处理,说白了就是逐帧处理图像。OpenCV用VideoCapture读视频,用VideoWriter写视频。

# 读取视频
cap = cv2.VideoCapture('video.mp4')

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break
    # 处理每一帧
    cv2.imshow('Frame', frame)
    if cv2.waitKey(25) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

保存视频时,编码格式是个大坑。你想想看,同样的.mp4后缀,编码器不对就写不进去。

编码格式 FourCC码 文件后缀
MPEG-4 cv2.VideoWriter_fourcc(*'XVID') .avi
H.264 cv2.VideoWriter_fourcc(*'H264') .mp4
Motion JPEG cv2.VideoWriter_fourcc(*'MJPG') .avi

注意:H.264编码在某些系统上可能不支持。我建议先用XVID测试,兼容性最好。

2.3 图像色彩空间转换

YOLO检测中,色彩空间转换是预处理的关键一步。最常用的是BGR转灰度、BGR转HSV。

# BGR 转灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# BGR 转 HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# BGR 转 RGB(用于matplotlib显示)
rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

为什么用HSV?因为HSV对光照变化更鲁棒。我在做交通标志检测时,用BGR阈值分割总是误检,换成HSV后准确率直接提升了15%。

核心要点:YOLO训练时输入通常是RGB,但OpenCV默认是BGR。记得在送入模型前做转换。

2.4 图像几何变换

几何变换包括缩放、旋转、平移、仿射变换和透视变换。YOLO的数据增强就大量用到这些操作。

# 缩放
resized = cv2.resize(img, (416, 416))

# 旋转(绕中心旋转45度)
(h, w) = img.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, 45, 1.0)
rotated = cv2.warpAffine(img, M, (w, h))

# 仿射变换
pts1 = np.float32([[50,50],[200,50],[50,200]])
pts2 = np.float32([[10,100],[200,50],[100,250]])
M = cv2.getAffineTransform(pts1, pts2)
affined = cv2.warpAffine(img, M, (w, h))
变换类型 函数 应用场景
缩放 cv2.resize() 统一输入尺寸
旋转 cv2.warpAffine() 数据增强
仿射 cv2.warpAffine() 矫正倾斜图像
透视 cv2.warpPerspective() 视角变换

避坑指南:缩放时注意保持宽高比,否则目标会变形。我习惯用cv2.INTER_AREA做缩小,用cv2.INTER_LINEAR做放大。

2.5 图像滤波与边缘检测

滤波是为了去噪,边缘检测是为了找到目标边界。这两个操作在YOLO预处理中经常配合使用。

# 高斯滤波
blurred = cv2.GaussianBlur(img, (5, 5), 0)

# 中值滤波
median = cv2.medianBlur(img, 5)

# Canny边缘检测
edges = cv2.Canny(blurred, 50, 150)

Canny边缘检测的参数怎么调?低阈值和高阈值。我一般设低阈值为50,高阈值为150。但实际项目中要根据图像对比度调整。有一次做工业零件检测,光照不均匀,我不得不把低阈值降到30才把边缘找全。

滤波方法 特点 适用场景
高斯滤波 平滑效果好,保留边缘 通用去噪
中值滤波 对椒盐噪声效果好 传感器噪声
双边滤波 保边去噪 人脸美化

实战建议:YOLO检测前,我通常先做高斯滤波去噪,再做Canny边缘检测。但注意,滤波太强会丢失细节,滤波太弱噪点太多。这个平衡需要根据实际数据调。

好了,这一章的基础操作就这些。别看它们简单,每个都是后续YOLO检测流水线的关键环节。你想想看,如果图像都读不对,色彩空间都搞混了,后面的检测结果能准吗?

我个人建议,学完这一章后,把每个操作都亲手敲一遍代码。遇到问题别怕,调试的过程就是成长的过程。我曾经为了一个滤波参数调了整整两天,但调通之后,对图像处理的理解就上了一个台阶。