昨天我们学习了计算机视觉的理论基础——知道了图像在计算机眼中是数字矩阵,理解了卷积如何提取特征,也看到了CNN如何一步步学会“看”。但问题来了:在实际项目中,你怎么把一张照片读进Python?怎么把彩色图转成灰度?怎么检测人脸?
今天,我们学习 OpenCV ——计算机视觉领域最基础、最强大、最普及的工具库。
OpenCV是什么?
OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉库,包含2500+优化算法,涵盖图像处理、视频分析、目标检测、机器学习等几乎所有视觉任务。
它就像视觉工程师的“瑞士军刀”——你遇到的大多数图像处理问题,OpenCV都有现成函数。
OpenCV的安装与核心理念
安装OpenCV
# 标准安装(包含主要模块)
pip install opencv-python
# 如果需要额外模块(如跟踪算法)
pip install opencv-contrib-python
# 验证安装
import cv2
print(f"OpenCV版本: {cv2.__version__}")
# 应输出 4.5.x 或更高版本
OpenCV的核心理念:一切皆矩阵
OpenCV读取的图像,本质上就是 NumPy数组——这意味着你可以用NumPy的所有功能操作图像!
import cv2
import numpy as np
# 读取图像
img = cv2.imread('photo.jpg')
print(type(img)) # <class 'numpy.ndarray'>
print(img.shape) # (高度, 宽度, 通道数) 例如 (480, 640, 3)
# 可以用NumPy方式操作
roi = img[100:200, 200:300] # 裁剪感兴趣区域
img[:, :, 0] = 0 # 将蓝色通道全部置0
关键认知:OpenCV + NumPy = 图像处理的“倚天剑+屠龙刀”。
图像的读写与显示 —— Hello World
读取图像:cv2.imread()
import cv2
# 最基本的读取——彩色模式(默认)
img_color = cv2.imread('cat.jpg')
# 等价于 img_color = cv2.imread('cat.jpg', cv2.IMREAD_COLOR)
# 灰度模式读取
img_gray = cv2.imread('cat.jpg', cv2.IMREAD_GRAYSCALE)
# 保留Alpha通道(透明度)
img_unchanged = cv2.imread('cat.png', cv2.IMREAD_UNCHANGED)
# 检查是否读取成功(文件路径错误时不会报错,但返回None)
if img_color is None:
print("错误:无法读取图像,请检查文件路径")
exit()
重要提示:OpenCV读取彩色图像时,默认通道顺序是 BGR(蓝-绿-红),而不是常见的RGB!这一点常被初学者忽视,导致用Matplotlib显示时颜色异常。
显示图像:cv2.imshow()
# 创建窗口并显示图像
cv2.imshow('My Cat', img_color)
# 等待按键(必须!否则窗口一闪而过)
cv2.waitKey(0) # 0表示无限等待,直到按下任意键
# 关闭所有窗口
cv2.destroyAllWindows()
cv2.waitKey()的秘密:
- 它不仅等待键盘输入,还负责处理GUI事件——没有它,图像窗口根本不会正常显示
- 可以传入毫秒数:
cv2.waitKey(5000) 等待5秒后自动关闭
保存图像:cv2.imwrite()
# 保存图像(格式由文件扩展名决定)
cv2.imwrite('cat_gray.jpg', img_gray) # 保存为JPEG
cv2.imwrite('cat_copy.png', img_color) # 保存为PNG
完整示例:读取→显示→保存
import cv2
# 读取图像
img = cv2.imread('cat.jpg', cv2.IMREAD_COLOR)
# 检查
if img is None:
print("文件读取失败")
exit()
# 显示原图
cv2.imshow('Original Cat', img)
# 转换为灰度并显示
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow('Gray Cat', gray)
# 保存灰度图
cv2.imwrite('cat_gray.jpg', gray)
# 等待按键
cv2.waitKey(0)
cv2.destroyAllWindows()
颜色空间转换 —— 让颜色更“听话”
为什么要转换颜色空间?
RGB虽然符合人类视觉,但在计算机视觉任务中并不是最佳选择:
- RGB三个通道高度相关,受光照影响大
- HSV(色调-饱和度-明度)将颜色信息与亮度分离,更适合基于颜色的目标提取
BGR ↔ 灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
BGR ↔ HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
HSV各通道范围(OpenCV中):
- H(色调):0~179
- S(饱和度):0~255
- V(明度):0~255
实战:提取蓝色物体
import cv2
import numpy as np
# 读取图像
img = cv2.imread('colorful_balls.jpg')
# 转换为HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 定义蓝色的HSV范围
lower_blue = np.array([110, 50, 50])
upper_blue = np.array([130, 255, 255])
# 创建掩膜(在范围内的像素为白色,其余为黑色)
mask = cv2.inRange(hsv, lower_blue, upper_blue)
# 将掩膜与原图进行“与”操作,只保留蓝色区域
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()
这段代码能做什么?—— 在彩色球堆中,只把蓝色的球“抠”出来,其他变黑。这是颜色追踪的基础。
图像的几何变换 —— 移动、旋转、缩放
缩放:cv2.resize()
# 按指定尺寸缩放
resized = cv2.resize(img, (300, 200))
# 按比例缩放
scale_percent = 50 # 缩小到50%
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
resized = cv2.resize(img, (width, height))
# 插值方法的选择
# 缩小:INTER_AREA 效果最好
# 放大:INTER_LINEAR 或 INTER_CUBIC
resized = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA)
平移:cv2.warpAffine()
# 定义平移矩阵 [1, 0, tx; 0, 1, ty]
tx, ty = 50, 100 # 向右移50,向下移100
M = np.float32([[1, 0, tx], [0, 1, ty]])
# 应用仿射变换
shifted = cv2.warpAffine(img, M, (img.shape[1], img.shape[0]))
旋转:cv2.getRotationMatrix2D()
# 获取旋转矩阵
h, w = img.shape[:2]
center = (w // 2, h // 2) # 旋转中心
angle = 45 # 逆时针45度
scale = 1.0 # 不缩放
M = cv2.getRotationMatrix2D(center, angle, scale)
# 应用旋转
rotated = cv2.warpAffine(img, M, (w, h))
仿射变换:三点对应
仿射变换是“线性变换+平移”,可以完成旋转、缩放、剪切等操作。OpenCV提供了根据三个对应点计算变换矩阵的函数:
# 原图中的三个点
pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
# 目标图中的三个点
pts2 = np.float32([[10, 100], [200, 50], [100, 250]])
# 计算仿射变换矩阵
M = cv2.getAffineTransform(pts1, pts2)
# 应用变换
dst = cv2.warpAffine(img, M, (img.shape[1], img.shape[0]))
透视变换:四点对应
当需要处理倾斜视角(如拍摄的文档、路牌)时,透视变换是利器:
# 原图中的四个角点(通常手动选取)
pts1 = np.float32([[56, 65], [368, 52], [28, 387], [389, 390]])
# 目标图中的四个角点(矩形)
pts2 = np.float32([[0, 0], [300, 0], [0, 300], [300, 300]])
# 计算透视变换矩阵
M = cv2.getPerspectiveTransform(pts1, pts2)
# 应用变换
dst = cv2.warpPerspective(img, M, (300, 300))
应用场景:扫描文档矫正、广告牌替换、AR效果。
图像滤波 —— 去噪与增强
低通滤波:平滑/模糊
# 均值滤波(每个像素替换为邻域平均值)
blur = cv2.blur(img, (5, 5))
# 高斯滤波(邻域加权平均,中心权重高)
gaussian = cv2.GaussianBlur(img, (5, 5), 1.5)
# 中值滤波(对椒盐噪声特别有效)
median = cv2.medianBlur(img, 5)
参数解释:
(5, 5):滤波器核大小(必须是奇数)
1.5:高斯核的标准差(σ)
高通滤波:边缘检测
作用:提取图像中灰度变化剧烈的地方——即边缘。
Sobel算子:一阶导数
# 计算x方向的梯度
sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=5)
# 计算y方向的梯度
sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=5)
# 计算梯度幅值
sobel = cv2.magnitude(sobelx, sobely)
sobel = cv2.convertScaleAbs(sobel) # 转为8位图像
Canny边缘检测:最常用的边缘算法
Canny算法包括高斯滤波 + 梯度计算 + 非极大值抑制 + 双阈值处理,效果通常最好。
# 简单的两行代码,效果惊人
edges = cv2.Canny(gray, threshold1=100, threshold2=200)
# 显示结果
cv2.imshow('Edges', edges)
参数含义:
threshold1:低阈值(低于此值的不是边缘)
threshold2:高阈值(高于此值的肯定是边缘)
- 中间的点,如果与高阈值边缘相连,则也被认为是边缘
视频处理 —— 让静态图像动起来
从摄像头捕获实时视频
import cv2
# 打开摄像头(0表示第一个摄像头)
cap = cv2.VideoCapture(0)
# 检查是否成功打开
if not cap.isOpened():
print("无法打开摄像头")
exit()
while True:
# 逐帧捕获
ret, frame = cap.read()
# 如果正确读取帧,ret为True
if not ret:
print("无法接收帧,退出...")
break
# 处理帧(例如转为灰度)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 显示结果
cv2.imshow('Camera', gray)
# 按'q'键退出
if cv2.waitKey(1) == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
从视频文件读取
cap = cv2.VideoCapture('video.mp4') # 只需替换为文件路径
# 其余代码与摄像头相同
保存视频
# 定义视频编码器和输出对象
fourcc = cv2.VideoWriter_fourcc(*'XVID') # 或 'MJPG', 'X264'
out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480))
while True:
ret, frame = cap.read()
if not ret:
break
# 处理帧(例如水平翻转)
frame = cv2.flip(frame, 1)
# 写入帧
out.write(frame)
cv2.imshow('Frame', frame)
if cv2.waitKey(1) == ord('q'):
break
# 释放所有资源
cap.release()
out.release()
cv2.destroyAllWindows()
实战项目——摄像头人脸检测与跟踪
让我们把今天学的所有知识整合起来,做一个完整的计算机视觉应用——实时人脸检测与跟踪。
核心思路
- 人脸检测:使用Haar级联分类器在每帧中查找人脸
- 人脸跟踪:检测到人脸后,用CSRT跟踪器持续追踪
- 状态切换:跟踪失败时,自动切换回检测模式
完整代码实现
import cv2
class FaceTracker:
"""实时人脸检测+跟踪器"""
def __init__(self):
# 加载预训练的人脸检测模型
self.face_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)
self.tracker = None
self.tracking = False
def detect_face(self, frame):
"""检测人脸"""
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = self.face_cascade.detectMultiScale(
gray,
scaleFactor=1.1, # 图像金字塔缩放比例
minNeighbors=5, # 每个候选框需要保留的邻域数
minSize=(30, 30) # 最小人脸尺寸
)
return faces
def init_tracker(self, frame, bbox):
"""初始化跟踪器"""
self.tracker = cv2.TrackerCSRT_create()
self.tracker.init(frame, tuple(bbox))
self.tracking = True
def update_tracker(self, frame):
"""更新跟踪"""
success, bbox = self.tracker.update(frame)
return success, bbox
def process_frame(self, frame):
"""处理单帧图像"""
# 如果当前未跟踪,执行检测
if not self.tracking:
faces = self.detect_face(frame)
if len(faces) > 0:
# 跟踪第一个检测到的人脸
bbox = faces[0] # (x, y, w, h)
self.init_tracker(frame, bbox)
# 绘制检测框
x, y, w, h = bbox
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.putText(frame, 'Detection', (x, y-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
else:
# 跟踪模式
success, bbox = self.update_tracker(frame)
if success:
x, y, w, h = [int(v) for v in bbox]
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
cv2.putText(frame, 'Tracking', (x, y-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
else:
# 跟踪失败,下一帧重新检测
self.tracking = False
return frame
# 主程序
def main():
cap = cv2.VideoCapture(0)
tracker = FaceTracker()
while True:
ret, frame = cap.read()
if not ret:
break
# 处理帧
processed_frame = tracker.process_frame(frame)
# 显示
cv2.imshow('Face Tracking', processed_frame)
# 按'q'退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
代码解析
| 组件 |
功能 |
技术点 |
| Haar检测 |
快速定位人脸 |
detectMultiScale参数调优 |
| CSRT跟踪 |
高精度跟踪 |
抗遮挡、尺度自适应 |
| 状态切换 |
检测→跟踪→检测 |
跟踪失败自动回退 |
运行效果:打开摄像头后,首先检测到人脸用绿色框标注,然后转为蓝色框跟踪;如果人脸移出画面或遮挡,系统自动重新检测。
从OpenCV开始,走进视觉世界
今天,我们学习了OpenCV最核心的基础知识:
| 模块 |
核心函数 |
应用场景 |
| 图像I/O |
imread, imshow, imwrite |
所有项目的起点 |
| 颜色空间 |
cvtColor, inRange |
颜色提取、目标追踪 |
| 几何变换 |
warpAffine, warpPerspective |
图像矫正、数据增强 |
| 滤波 |
blur, Sobel, Canny |
去噪、边缘检测 |
| 视频 |
VideoCapture, VideoWriter |
实时处理、视频分析 |
| 检测跟踪 |
CascadeClassifier, Tracker |
人脸识别、目标追踪 |
OpenCV最大的价值:它不是黑箱,而是工具箱——你清楚地知道每个函数在做什么,并且可以组合它们解决实际问题。这篇文章只是帮你打开了 计算机视觉 这扇大门,想了解更多实战项目和进阶技巧,欢迎在 云栈社区 继续探索和交流。