CV 概览与数字图像基础:RGB/HSV 颜色空间、像素矩阵与位深度
引言
计算机视觉(Computer Vision,CV)是人工智能三大感知分支之一,核心任务是让计算机“看明白”世界——从图像和视频中提取结构化信息,完成认知层面的理解。
但在啃下目标检测、图像分割、Transformer 大模型这些“硬骨头”之前,先把「图像到底是什么」「计算机怎么存储图像」「怎么把颜色给计算机说清楚」这三件事弄懂,是 100% 必要的。本文用大白话 + OpenCV 代码,一次性讲透这三个核心底层知识。
📂 所属阶段:第一阶段 — 图像处理基石(传统 CV 篇)
🔗 相关章节:OpenCV 快速入门 · 图像增强与滤波
1. 什么是计算机视觉?
1.1 CV 的三个处理层次
按照抽象程度和处理复杂度,CV 任务通常分为三个层次,层层递进:
也就是说,低层视觉处理的是图像的“原料”,中层视觉开始提取“零件”,而高层视觉负责输出对整张图的理解。
1.2 高频应用场景(身边的 CV)
2. 数字图像的本质:像素矩阵
2.1 从“照片”到“矩阵”
现实世界的照片是连续的光信号,但计算机只能处理离散的数字。数码相机或扫描仪会通过两步操作把光变成矩阵:
- 采样:把照片划分成 H(高)× W(宽)个小格子——每个格子就叫一个像素(Pixel)
- 量化:给每个像素的亮度或颜色分配一个数值(通常是一个固定范围内的整数)
这样一来,一张图像在计算机眼中就变成了一个数字矩阵。
灰度图像 vs 彩色图像
为了方便理解,我们用 NumPy 来手动创建一些纯色图像:
import numpy as np
import cv2
# 1. 创建100×100的纯黑灰度图(全0)
black_gray = np.zeros((100, 100), dtype=np.uint8)
# 创建100×100的纯白灰度图(全255)
white_gray = np.full((100, 100), 255, dtype=np.uint8)
# 2. 创建100×100×3的纯黑RGB图(注意:OpenCV 默认 BGR,这里先按概念说 RGB)
black_rgb = np.zeros((100, 100, 3), dtype=np.uint8)
# 创建100×100×3的红色图(后面会专门说明 OpenCV 的 BGR 顺序!)
red_rgb = np.zeros((100, 100, 3), dtype=np.uint8)
red_rgb[:, :, 0] = 255 # 假设最后一维索引0是R(但 OpenCV 里其实是 B)
print(f"灰度图维度: {black_gray.shape}")
print(f"彩色图维度: {black_rgb.shape}")
可以看到,灰度图就是一个简单的二维矩阵,而彩色图则是一个“三维”数组——宽、高以及三个颜色通道。
2.2 位深度:决定图像的“精细度”
位深度(Bit Depth)指的是每个像素通道用多少位二进制数存储。位数越多,能表示的灰度或颜色等级就越丰富:
- 8 位:最常用!每个通道取值范围 0 ~ 255,组合起来约 1678 万种 RGB 颜色
- 16 位:医学影像、天文图像专用,每个通道范围 0 ~ 65535
- 32 位浮点数:深度学习和高精度处理常用,亮度通常归一化到 0.0 ~ 1.0 之间
直接生成三种位深度的随机图,观察它们的类型和范围:
import numpy as np
# 生成3种位深度的随机图
img_8bit = np.random.randint(0, 256, (50, 50), dtype=np.uint8)
img_16bit = np.random.randint(0, 65536, (50, 50), dtype=np.uint16)
img_32float = np.random.rand(50, 50).astype(np.float32)
print(f"8位图的数据类型: {img_8bit.dtype}, 最小值: {img_8bit.min()}, 最大值: {img_8bit.max()}")
print(f"16位图的数据类型: {img_16bit.dtype}, 最小值: {img_16bit.min()}, 最大值: {img_16bit.max()}")
print(f"32位浮点图的数据类型: {img_32float.dtype}, 最小值: {img_32float.min():.4f}, 最大值: {img_32float.max():.4f}")
3. 两种核心颜色空间
3.1 RGB:计算机最“舒服”的格式
RGB(Red, Green, Blue)基于光的加色混合——红、绿、蓝三束光以不同比例叠加,就能形成人眼能感知的绝大多数颜色。这是显示器、图像文件的原生存储格式。
常见 RGB / BGR 值(OpenCV 用 BGR!)
OpenCV 颜色空间转换的关键坑
因为历史原因,OpenCV 的彩色图像通道顺序是 BGR 而不是 RGB。这一点非常容易踩坑,尤其是当你把 OpenCV 和 Matplotlib、Pillow 等库搭配使用时,一定要先做转换。
import cv2
import numpy as np
# 假设读入一张真实图片
img_bgr = cv2.imread("test.jpg")
# 核心转换函数
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # 用于 matplotlib 显示
img_gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) # 转灰度
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV) # 转 HSV(下一节重点)
# 简单验证:手动切片反转通道
img_rgb_manual = img_bgr[:, :, ::-1] # 第三个维度(通道)倒序
print(np.array_equal(img_rgb, img_rgb_manual)) # 应该输出 True
记忆口诀:如果你直接用 Matplotlib 显示 img_bgr,红色和蓝色会互换,图像会变得很奇怪。记住:BGR 转 RGB 只需倒序最后一个维度。
3.2 HSV:人类最“直观”的格式
HSV(Hue, Saturation, Value)将颜色属性分离,更贴近我们日常描述颜色的方式——例如“这个红色再鲜艳一点”“那个蓝色再暗一点”。因此,在做颜色分割、物体跟踪时,HSV 通常是首选。
三个分量的含义(OpenCV 专属值域!)
注意:Hue 的范围是 0 ~ 179 而不是 0 ~ 360,这是因为 OpenCV 为了 fit 到 8 位整数范围(0 ~ 255),把 360° 的色相轮压缩到 0 ~ 180。
实战:用 HSV 做红色物体检测
红色在色轮上处于 350° ~ 10° 的范围,映射到 OpenCV 里就变成了两个区间:0 ~ 10 和 170 ~ 180。因此检测红色需要分别处理这两段,然后把掩码合并。
下面是一个完整的红色检测函数,你可以直接拿到自己的图片上试用:
import cv2
import numpy as np
def detect_red(image_path):
# 1. 读入并转 HSV
img_bgr = cv2.imread(image_path)
if img_bgr is None:
print("请输入正确的图片路径!")
return None, None, None
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)
# 2. 定义红色的 HSV 两个区间
lower1 = np.array([0, 100, 100])
upper1 = np.array([10, 255, 255])
lower2 = np.array([170, 100, 100])
upper2 = np.array([180, 255, 255])
# 3. 生成掩码(mask):红色区域为白色(255),其他为黑色(0)
mask1 = cv2.inRange(img_hsv, lower1, upper1)
mask2 = cv2.inRange(img_hsv, lower2, upper2)
mask = cv2.bitwise_or(mask1, mask2) # 合并两个区间
# 4. 用掩码提取红色区域
result = cv2.bitwise_and(img_bgr, img_bgr, mask=mask)
return img_bgr, mask, result
# 本地运行时取消注释即可
# original, mask, res = detect_red("red_flower.jpg")
# cv2.imshow("原图", original)
# cv2.imshow("红色掩码", mask)
# cv2.imshow("红色检测结果", res)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
调整 lower 和 upper 中的饱和度(S)、亮度(V)阈值,可以控制对浅色/暗色的宽容度,是你自己调参时的主要发力点。
4. 实战项目:快速分析一张图像的信息
把前面学到的知识点整合成一个实用函数,只要输入图片路径,就能一次性打印出图像的所有核心属性:
import cv2
def analyze_image(image_path):
img = cv2.imread(image_path)
if img is None:
print("❌ 图片读取失败,请检查路径!")
return
# 1. 基础属性
h, w = img.shape[:2]
channels = img.shape[2] if len(img.shape) == 3 else 1
dtype = img.dtype
print(f"✅ 图像基本信息:")
print(f" - 尺寸: {w} × {h}")
print(f" - 通道数: {channels}")
print(f" - 数据类型: {dtype}")
print(f" - 总像素数: {w * h:,}") # 加逗号分隔大数字
# 2. BGR 各通道的统计(仅彩色图)
if channels == 3:
print(f"\n✅ BGR 通道统计:")
for idx, name in enumerate(['Blue', 'Green', 'Red']):
ch = img[:, :, idx]
print(f" - {name}: min={ch.min()}, max={ch.max()}, mean={ch.mean():.2f}")
# 本地运行示例
# analyze_image("test.jpg")
这个函数可以帮你快速了解一张图片的数据结构,也很适合用来调试后续的图像处理算法。
5. 总结
核心知识点回顾
- 图像本质:连续的光信号经过采样和量化后,变成离散的像素矩阵(H×W 或 H×W×3)
- 位深度:常用 8 位(0 ~ 255),位数越多,图像细节越丰富
- 颜色空间:
- RGB / BGR:计算机原生格式,硬件友好,但颜色耦合度高
- HSV:符合人类直觉,颜色分离,是颜色检测、分割的首选
- OpenCV 坑点:默认 BGR 通道顺序,与其他库配合时务必通过
cv2.cvtColor 或切片倒序转换
💡 最后提醒:像素矩阵是计算机视觉的基石!所有高级算法——包括卷积神经网络(CNN)——本质上都是在“玩矩阵”:加减乘除、卷积、池化……建议多手动生成小矩阵、修改像素值,尽早建立直观的“矩阵感”!
相关教程