第2章 摄像头基础与OpenCV入门
摄像头,说白了就是计算机的「眼睛」。做视觉开发这么多年,我见过太多人一上来就调API,结果连摄像头为什么卡顿、画面为什么偏色都搞不清楚。这一章,咱们先把摄像头的工作原理摸透,再上手OpenCV这个最常用的视觉工具库。
2.1 摄像头工作原理
摄像头采集图像的流程,其实就三步:光电转换 → 模数转换 → 图像输出。光线通过镜头打到感光元件(CMOS或CCD)上,感光元件把光信号转成电信号,再经过ADC(模数转换器)变成数字信号,最后通过USB或MIPI接口传给主机。
| 组件 | 作用 | 常见参数 |
|---|---|---|
| 镜头 | 聚焦光线,控制进光量 | 焦距、光圈、视场角 |
| 感光元件 | 光电转换,决定分辨率 | 像素数、像元尺寸、帧率 |
| ISP(图像信号处理器) | 自动白平衡、降噪、锐化 | 处理能力、支持的格式 |
| 接口 | 传输图像数据到主机 | USB 2.0/3.0、MIPI、GigE |
2.2 OpenCV是什么?
OpenCV全称Open Source Computer Vision Library,开源计算机视觉库。它封装了上千种图像处理和计算机视觉算法,从最基本的读写图片,到人脸检测、物体追踪、相机标定,全都有现成接口。
为什么选OpenCV?三个理由:
- 跨平台:Windows、Linux、macOS、Android、iOS全支持
- 性能好:底层用C++写的,Python接口只是封装,核心运算效率很高
- 生态成熟:文档全、社区大、踩过的坑基本都能搜到答案
安装命令很简单:
pip install opencv-python
pip install opencv-contrib-python # 包含额外模块
建议两个都装。contrib里有很多实用功能,比如人脸识别模块face。
2.3 读取摄像头视频流
读取摄像头,核心就一个类:cv2.VideoCapture。参数传0表示默认摄像头,传1表示第二个摄像头,也可以传视频文件路径。
import cv2
# 打开摄像头
cap = cv2.VideoCapture(0)
# 检查是否成功打开
if not cap.isOpened():
print("摄像头打开失败")
exit()
while True:
# 读取一帧
ret, frame = cap.read()
if not ret:
print("读取帧失败")
break
# 显示画面
cv2.imshow('Camera', frame)
# 按q键退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
cap.release() 会导致摄像头一直被占用,下次打开会报错。还有 cv2.waitKey() 的参数,1表示每帧等待1毫秒,设成0画面会卡死。
为什么会卡顿?最常见的原因是读取帧的速度跟不上显示速度。你可以用 cap.set(cv2.CAP_PROP_FPS, 30) 设置帧率,或者用 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) 降低分辨率。
| 属性 | 含义 | 常用值 |
|---|---|---|
| CAP_PROP_FRAME_WIDTH | 画面宽度 | 640, 1280, 1920 |
| CAP_PROP_FRAME_HEIGHT | 画面高度 | 480, 720, 1080 |
| CAP_PROP_FPS | 帧率 | 15, 25, 30 |
| CAP_PROP_BRIGHTNESS | 亮度 | 0-255 |
2.4 显示与保存视频帧
显示用 cv2.imshow(),保存用 cv2.imwrite()。这两个函数我几乎每天都会用到。
import cv2
import os
cap = cv2.VideoCapture(0)
frame_count = 0
save_dir = 'captured_frames'
os.makedirs(save_dir, exist_ok=True)
while True:
ret, frame = cap.read()
if not ret:
break
# 显示当前帧
cv2.imshow('Live', frame)
key = cv2.waitKey(1) & 0xFF
# 按s键保存当前帧
if key == ord('s'):
filename = f'{save_dir}/frame_{frame_count:04d}.jpg'
cv2.imwrite(filename, frame)
print(f'已保存: {filename}')
frame_count += 1
# 按q键退出
elif key == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
你想想看,如果每帧都保存,一分钟就是1800张图。所以实际项目中我一般用帧差法或运动检测,只在画面有变化时才保存。这个后面章节会详细讲。
2.5 本章知识体系
下面这张图把摄像头采集到OpenCV处理的完整链路画出来了,建议你对照着理解:
2.6 避坑指南
- 摄像头权限:macOS和Linux需要给Python授权,Windows有时会被其他应用占用。我习惯在代码开头加一段检测逻辑。
- 分辨率设置:不是所有摄像头都支持你设置的分辨率。建议先
cap.get()读取实际值,再决定要不要调整。 - 帧率与延迟:USB 2.0摄像头在1080P下很难跑到30fps,实测一般20fps左右。追求实时性建议用640×480。
- 内存泄漏:长时间运行不释放资源,内存会持续增长。每处理完一批帧,手动调用
cap.release()是个好习惯。
这一章的内容,说白了就是让你能「看到」摄像头画面。别小看这几行代码,后面的人脸检测、人脸识别、活体检测,全都要建立在稳定的视频流基础上。先把基础循环跑通,再谈优化。