第2章 摄像头基础与OpenCV入门

摄像头,说白了就是计算机的「眼睛」。做视觉开发这么多年,我见过太多人一上来就调API,结果连摄像头为什么卡顿、画面为什么偏色都搞不清楚。这一章,咱们先把摄像头的工作原理摸透,再上手OpenCV这个最常用的视觉工具库。

2.1 摄像头工作原理

摄像头采集图像的流程,其实就三步:光电转换 → 模数转换 → 图像输出。光线通过镜头打到感光元件(CMOS或CCD)上,感光元件把光信号转成电信号,再经过ADC(模数转换器)变成数字信号,最后通过USB或MIPI接口传给主机。

组件 作用 常见参数
镜头 聚焦光线,控制进光量 焦距、光圈、视场角
感光元件 光电转换,决定分辨率 像素数、像元尺寸、帧率
ISP(图像信号处理器) 自动白平衡、降噪、锐化 处理能力、支持的格式
接口 传输图像数据到主机 USB 2.0/3.0、MIPI、GigE
我个人的经验:做人脸识别项目,分辨率不是越高越好。1080P足够,再高反而增加处理延迟。帧率建议25-30fps,低于15fps人脸检测会明显卡顿。

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()
保存格式建议:人脸识别场景用JPG就行,压缩率设95以上画质损失很小。需要做后续处理的场景建议用PNG,无损格式更保险。

你想想看,如果每帧都保存,一分钟就是1800张图。所以实际项目中我一般用帧差法或运动检测,只在画面有变化时才保存。这个后面章节会详细讲。

2.5 本章知识体系

下面这张图把摄像头采集到OpenCV处理的完整链路画出来了,建议你对照着理解:

摄像头采集 → OpenCV处理 完整流程 镜头 + 感光元件 ISP 图像处理 USB/MIPI 接口 主机内存 cv2.VideoCapture cap.read() 获取帧 cv2.imshow 显示 cv2.imwrite 保存 常见问题 卡顿 / 偏色 / 无法打开 解决方案 调分辨率 / 设帧率 / 检查权限 最佳实践 释放资源 / 帧差保存 核心思路:硬件采集 → OpenCV读取 → 显示/保存 → 异常处理,每一步都有坑,别跳 建议先跑通基础循环,再逐步加入帧处理、检测、保存等逻辑

2.6 避坑指南

  • 摄像头权限:macOS和Linux需要给Python授权,Windows有时会被其他应用占用。我习惯在代码开头加一段检测逻辑。
  • 分辨率设置:不是所有摄像头都支持你设置的分辨率。建议先 cap.get() 读取实际值,再决定要不要调整。
  • 帧率与延迟:USB 2.0摄像头在1080P下很难跑到30fps,实测一般20fps左右。追求实时性建议用640×480。
  • 内存泄漏:长时间运行不释放资源,内存会持续增长。每处理完一批帧,手动调用 cap.release() 是个好习惯。

这一章的内容,说白了就是让你能「看到」摄像头画面。别小看这几行代码,后面的人脸检测、人脸识别、活体检测,全都要建立在稳定的视频流基础上。先把基础循环跑通,再谈优化。