2、图像与视频基础:像素、色彩空间、文件格式与帧率
好,咱们正式开始聊图像和视频的基础。这部分内容,说白了就是整个计算机视觉的「地基」。你想想看,不管后面做多酷炫的检测、跟踪、识别,最终处理的都是这些最基础的东西。我个人习惯,在动手写代码之前,一定先把这些概念理清楚,不然踩坑了都不知道怎么爬出来。
2.1 像素:图像的最小单元
图像是什么?在计算机眼里,图像就是一个巨大的数字矩阵。这个矩阵里的每一个小格子,就是一个像素(Pixel)。
你可以把像素想象成马赛克瓷砖。一张1920x1080的图像,就是由1920列、1080行,总共超过200万块小瓷砖拼出来的。每个瓷砖都有自己的颜色值。
我在项目中遇到过一个问题:有同学把一张4K图片直接缩放到100x100,结果图像完全没法看。为什么?因为像素点被暴力合并了,细节全丢了。所以,处理图像时一定要清楚你的像素数量够不够用。
核心概念: 像素值范围通常是0-255(8位图像)。0代表全黑,255代表全亮。如果是16位图像,范围就是0-65535。
2.2 色彩空间:BGR、RGB、HSV、灰度
色彩空间,说白了就是「用数学描述颜色」的方式。不同的场景,用不同的色彩空间会更顺手。
2.2.1 BGR 与 RGB
OpenCV默认用的是BGR,不是我们常见的RGB。这一点坑了无数新手。
我记得第一次用OpenCV显示图像时,发现人脸是蓝色的。查了半天,原来是OpenCV读进来是BGR,而matplotlib显示需要RGB。嗯,这里要注意:OpenCV的imread()返回的是BGR顺序。
import cv2
import matplotlib.pyplot as plt
# 读取图像,默认是BGR
img = cv2.imread('face.jpg')
# 转换为RGB才能正确显示
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)
plt.show()
避坑指南: 我曾经在做一个项目时,用OpenCV读取图像后直接保存,结果颜色完全不对。后来发现是保存时用了BGR格式,而预览软件默认按RGB解析。所以,保存图像前一定要确认色彩空间。
2.2.2 HSV 色彩空间
HSV(色调Hue、饱和度Saturation、明度Value)是我个人最喜欢的色彩空间。为什么?因为它更符合人类对颜色的感知。
在BGR里,你要找到某个颜色范围很麻烦。比如要检测红色,BGR的红色可能是(0,0,255),但稍微暗一点、亮一点,数值就变了。而在HSV里,你只需要固定H值(色调),调整S和V就能覆盖所有红色。
我建议在做颜色检测时,优先用HSV。比如检测交通灯:
import cv2
import numpy as np
# 读取图像
img = cv2.imread('traffic_light.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 红色范围(H: 0-10 和 170-180)
lower_red1 = np.array([0, 50, 50])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([170, 50, 50])
upper_red2 = np.array([180, 255, 255])
# 生成掩码
mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
mask = cv2.bitwise_or(mask1, mask2)
# 提取红色区域
result = cv2.bitwise_and(img, img, mask=mask)
小技巧: HSV中H的范围是0-180(OpenCV为了节省空间,把0-360压缩了一半)。S和V的范围是0-255。记住这个,不然你设的阈值永远不对。
2.2.3 灰度图像
灰度图像只有一个通道,值从0(黑)到255(白)。为什么需要灰度?因为很多算法在灰度图上跑得更快,而且不受颜色干扰。
比如边缘检测、人脸识别,通常第一步就是把彩色图转成灰度图。你想想看,三个通道的数据量是单通道的三倍,处理起来自然慢。
# 彩色转灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 或者直接以灰度模式读取
gray_img = cv2.imread('photo.jpg', cv2.IMREAD_GRAYSCALE)
2.3 图像与视频文件格式
格式这东西,说白了就是「怎么把像素数据存到硬盘里」。不同的格式有不同的压缩方式,直接影响文件大小和画质。
| 格式 | 特点 | 适用场景 |
|---|---|---|
| BMP | 无压缩,文件巨大 | 几乎不用,除非要绝对无损 |
| JPEG/JPG | 有损压缩,文件小,画质可接受 | 照片、网络传输 |
| PNG | 无损压缩,支持透明通道 | 图标、需要透明背景的图像 |
| TIFF | 无损,支持多页 | 专业摄影、医学影像 |
视频格式呢?视频其实就是一帧一帧的图像快速播放。常见的视频容器格式有MP4、AVI、MOV等。而编码格式(比如H.264、H.265)决定了怎么压缩这些帧。
我记得有一次做实时视频分析,用AVI格式保存,结果文件大得离谱。后来换成MP4+H.264编码,文件大小直接缩到十分之一,画质几乎没变。
经验之谈: OpenCV保存视频时,需要指定编码器。常用的编码器是'XVID'(对应AVI)和'avc1'(对应MP4)。如果编码器不对,保存的视频可能打不开。
# 保存视频示例
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.avi', fourcc, 20.0, (640, 480))
while True:
ret, frame = cap.read()
if not ret:
break
out.write(frame)
out.release()
2.4 帧率与分辨率
这两个概念,是视频分析的核心参数。
帧率(FPS):每秒显示的图像帧数。24FPS是电影标准,30FPS是电视标准,60FPS是流畅标准。做实时分析时,帧率决定了你的算法能不能跟上视频速度。
分辨率:图像的宽高像素数。常见的有:
- 640x480(VGA):老式摄像头,现在基本淘汰
- 1280x720(720p):高清入门
- 1920x1080(1080p):全高清,目前主流
- 3840x2160(4K):超高清,但处理起来很吃性能
你想想看,处理1080p的视频,每帧有200多万像素。如果算法每帧要处理10毫秒,那最多只能跑到100FPS。但如果分辨率升到4K,像素量翻四倍,处理时间也跟着翻倍,帧率就掉下来了。
避坑指南: 我曾经在做一个实时人脸检测项目时,用了4K摄像头,结果算法跑起来只有5FPS,完全没法用。后来把分辨率降到720p,帧率直接飙到30FPS。所以,实时应用一定要权衡分辨率和帧率。
怎么在OpenCV里获取和设置这些参数?
import cv2
cap = cv2.VideoCapture(0) # 打开摄像头
# 获取参数
fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
print(f"帧率: {fps}, 分辨率: {width}x{height}")
# 设置参数(不一定所有摄像头都支持)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
cap.set(cv2.CAP_PROP_FPS, 30)
小技巧: 有些摄像头不支持你设置的分辨率,它会自动选一个最接近的。所以设置完后,最好再读一遍确认一下。
好了,这一章的内容就这些。像素、色彩空间、文件格式、帧率分辨率,这些基础概念搞清楚了,后面学起来就顺了。下一章我们开始动手,用OpenCV读写图像和视频,到时候你会发现自己已经能看懂很多代码了。