CV 概览与数字图像基础:RGB/HSV 颜色空间、像素矩阵与位深度

引言

计算机视觉(Computer Vision,CV)是人工智能三大感知分支之一,核心任务是让计算机“看明白”世界——从图像和视频中提取结构化信息,完成认知层面的理解。

但在啃下目标检测、图像分割、Transformer 大模型这些“硬骨头”之前,先把「图像到底是什么」「计算机怎么存储图像」「怎么把颜色给计算机说清楚」这三件事弄懂,是 100% 必要的。本文用大白话 + OpenCV 代码,一次性讲透这三个核心底层知识。

📂 所属阶段:第一阶段 — 图像处理基石(传统 CV 篇)
🔗 相关章节OpenCV 快速入门 · 图像增强与滤波


1. 什么是计算机视觉?

1.1 CV 的三个处理层次

按照抽象程度和处理复杂度,CV 任务通常分为三个层次,层层递进:

层次输入 → 输出核心功能代表技术
低层视觉图像 → 图像 / 低维信号像素级直接操作,不损失空间结构滤波、去噪、边缘/角点检测
中层视觉图像 → 结构化 / 半结构化信息将像素转化为“有意义的物体部件”特征匹配、目标检测、图像分割
高层视觉图像 → 语义 / 认知结果赋予图像“人类能理解的含义”场景理解、行为识别、图像描述

也就是说,低层视觉处理的是图像的“原料”,中层视觉开始提取“零件”,而高层视觉负责输出对整张图的理解。

1.2 高频应用场景(身边的 CV)

领域典型应用
移动互联网美颜滤镜、扫码支付、人脸解锁
自动驾驶车道线检测、行人检测、交通标志识别
工业质检缺陷检测、零件计数、尺寸测量
医疗健康肿瘤影像分割、眼底病变筛查

2. 数字图像的本质:像素矩阵

2.1 从“照片”到“矩阵”

现实世界的照片是连续的光信号,但计算机只能处理离散的数字。数码相机或扫描仪会通过两步操作把光变成矩阵:

  1. 采样:把照片划分成 H(高)× W(宽)个小格子——每个格子就叫一个像素(Pixel)
  2. 量化:给每个像素的亮度或颜色分配一个数值(通常是一个固定范围内的整数)

这样一来,一张图像在计算机眼中就变成了一个数字矩阵

灰度图像 vs 彩色图像

图像类型存储矩阵维度每个元素含义常用值域(8 位)
灰度H × W像素的灰度值0(黑)~ 255(白)
彩色 RGBH × W × 3R / G / B 三个通道的亮度每个通道 0 ~ 255

为了方便理解,我们用 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!)

颜色标准 RGBOpenCV BGR
(255, 0, 0)(0, 0, 255)
绿(0, 255, 0)(0, 255, 0)
(0, 0, 255)(255, 0, 0)
(255, 255, 255)同上
灰(中)(128, 128, 128)同上

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 专属值域!)

分量含义OpenCV 值域
Hue(色调)基本颜色(红→黄→绿→青→蓝→紫→红)0 ~ 179(压缩了 360° 的标准色轮)
Saturation(饱和度)颜色的“纯度/鲜艳度”,0 是灰色,255 是纯色0 ~ 255
Value(亮度)颜色的“明暗程度”,0 是黑色,255 是最亮0 ~ 255

注意:Hue 的范围是 0 ~ 179 而不是 0 ~ 360,这是因为 OpenCV 为了 fit 到 8 位整数范围(0 ~ 255),把 360° 的色相轮压缩到 0 ~ 180。

实战:用 HSV 做红色物体检测

红色在色轮上处于 350° ~ 10° 的范围,映射到 OpenCV 里就变成了两个区间:0 ~ 10170 ~ 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()

调整 lowerupper 中的饱和度(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. 总结

核心知识点回顾

  1. 图像本质:连续的光信号经过采样和量化后,变成离散的像素矩阵(H×W 或 H×W×3)
  2. 位深度:常用 8 位(0 ~ 255),位数越多,图像细节越丰富
  3. 颜色空间
    • RGB / BGR:计算机原生格式,硬件友好,但颜色耦合度高
    • HSV:符合人类直觉,颜色分离,是颜色检测、分割的首选
  4. OpenCV 坑点:默认 BGR 通道顺序,与其他库配合时务必通过 cv2.cvtColor 或切片倒序转换

💡 最后提醒:像素矩阵是计算机视觉的基石!所有高级算法——包括卷积神经网络(CNN)——本质上都是在“玩矩阵”:加减乘除、卷积、池化……建议多手动生成小矩阵、修改像素值,尽早建立直观的“矩阵感”!


相关教程