4. 图像基础与色彩空间:像素、分辨率与色彩转换

各位同学,今天我们来聊聊图像预处理里最基础、也最容易被忽视的一块——像素与色彩空间。我刚开始做工业视觉那会儿,总觉得这些概念太简单,结果在项目里栽过不少跟头。说白了,你连图像里每个像素存的是什么数据都不清楚,后面做再好的模型也是白搭。

4.1 像素与分辨率:图像的最小单元

一张数字图像,本质上就是一个二维数组。数组里的每个元素,就是一个像素。像素是图像的最小信息单元,它记录的是该位置的颜色或亮度值。

分辨率则决定了图像的精细程度。工业相机选型时,分辨率是第一个要敲定的参数。

分辨率类型 定义 工业场景举例
空间分辨率 单位长度内的像素数(如 dpi、ppi) PCB 检测要求 0.05mm/pixel
图像分辨率 图像的总像素数(如 1920×1080) 500 万像素相机用于精密零件测量
位深 每个像素用多少 bit 表示 8bit(0-255)vs 10bit(0-1023)
我的经验:别盲目追求高分辨率。分辨率越高,图像数据量越大,处理速度会直线下降。我曾经在一个缺陷检测项目里,把 2000 万像素的图直接喂进模型,结果训练时间长了 3 倍,精度只提升了 0.5%。后来降采样到 500 万像素,效果反而更稳定。

4.2 BGR 与 RGB:OpenCV 的“坑”

如果你用过 OpenCV,一定被它坑过——它默认的通道顺序是 BGR,而不是我们熟悉的 RGB。为什么?因为早期相机硬件厂商习惯用 BGR 顺序存储数据,OpenCV 沿用了这个传统。

但深度学习框架(比如 PyTorch、TensorFlow)的预训练模型,几乎都是用 RGB 训练的。这就导致了一个经典问题:你直接拿 OpenCV 读图,然后送进模型,结果完全不对。

import cv2
import matplotlib.pyplot as plt

# OpenCV 默认 BGR
img_bgr = cv2.imread('example.jpg')

# 显示时如果不转换,颜色会偏蓝
plt.subplot(1, 2, 1)
plt.imshow(img_bgr)  # 错误显示

# 转换为 RGB
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
plt.subplot(1, 2, 2)
plt.imshow(img_rgb)  # 正确显示
避坑指南:我曾经在部署一个缺陷检测模型时,训练时用的 RGB,推理时忘了转换 BGR,结果模型把正常产品全判成了缺陷。排查了整整两天才发现是通道顺序的问题。记住:训练和推理的通道顺序必须一致。

4.3 灰度图与二值图:简化信息,突出结构

灰度图去掉了颜色信息,只保留亮度。每个像素值范围 0(黑)到 255(白)。工业视觉里,很多场景根本不需要颜色——比如测量尺寸、检测划痕、定位边缘。

二值图更进一步,像素只有 0 和 1(或 0 和 255)。它是图像分割的最终结果,也是很多形态学操作的基础。

# 灰度化
gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY)

# 二值化(固定阈值)
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# 二值化(自适应阈值——光照不均时更鲁棒)
adaptive_binary = cv2.adaptiveThreshold(
    gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
    cv2.THRESH_BINARY, 11, 2
)
什么时候用灰度图?
  • 形状检测、尺寸测量、边缘提取
  • 纹理分析、表面缺陷检测(如划痕、凹坑)
  • OCR 文字识别前的预处理
什么时候必须用彩色?
  • 颜色分选(如水果成熟度、药品胶囊颜色)
  • 多通道融合(如红外+可见光)
  • 需要利用颜色特征的深度学习模型

4.4 HSV 色彩空间:更符合人眼直觉

RGB 虽然直观,但它不适合做颜色分割。为什么?因为 RGB 的三个通道高度相关,光照一变,三个值一起变,你很难设定一个固定的阈值。

HSV 把颜色分解为:

  • H(Hue,色调):颜色的种类,用角度表示(0°-180° 在 OpenCV 中)
  • S(Saturation,饱和度):颜色的纯度
  • V(Value,明度):亮度

你想想看,在 HSV 空间里做颜色检测,你只需要设定 H 的范围,S 和 V 可以适当放宽——光照变化主要影响 V,不影响 H。这就稳定多了。

# RGB 转 HSV
hsv = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2HSV)

# 设定红色范围(OpenCV 中 H 范围 0-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)
我的习惯:做颜色检测时,我会先用一个取色工具(比如 OpenCV 的鼠标回调)在图像上采样几个点,看看它们的 HSV 值分布,再确定阈值范围。别凭感觉设阈值,数据说话。

4.5 Lab 色彩空间:感知均匀,适合度量颜色差异

Lab 色彩空间的设计目标是感知均匀——也就是说,两个颜色在 Lab 空间里的欧氏距离,与人眼感知到的颜色差异成正比。这在工业视觉里非常有用,比如检测产品颜色是否合格。

  • L:亮度(0-100)
  • a:红绿色度(负值偏绿,正值偏红)
  • b:黄蓝色度(负值偏蓝,正值偏黄)
# RGB 转 Lab(注意 OpenCV 需要先转 float32)
lab = cv2.cvtColor(img_rgb.astype(np.float32) / 255.0,
                   cv2.COLOR_RGB2Lab)

# 计算两个颜色的色差(Delta E)
color1 = lab[100, 200]  # 参考颜色
color2 = lab[150, 250]  # 待测颜色
delta_e = np.sqrt(np.sum((color1 - color2) ** 2))
print(f'色差 Delta E = {delta_e:.2f}')
注意:Lab 空间不是线性空间,直接做算术运算有时会出问题。另外,OpenCV 的 Lab 取值范围和标准 CIE Lab 不完全一致,需要做归一化。我建议在做颜色匹配时,先用标准色卡标定一下。

4.6 知识体系总览

下面这张图总结了本章的核心知识点和它们之间的关系。你可以把它当作一个快速索引。

图像基础与色彩空间知识体系 图像基础与色彩空间知识体系 数字图像 像素与分辨率 色彩空间 灰度图与二值图 空间分辨率 图像分辨率 BGR / RGB HSV Lab 固定阈值 自适应阈值 应用场景:缺陷检测 | 尺寸测量 | 颜色分选 | OCR | 目标定位 💡 核心原则:训练与推理的通道顺序必须一致 颜色检测优先用 HSV,颜色差异度量用 Lab

4.7 本章小结

图像预处理不是花架子,它直接决定了后续模型能不能学到有效特征。我见过太多项目,模型调了半天没效果,最后发现是色彩空间用错了。嗯,这里要注意:

  • 像素和分辨率决定了你能看到多细的细节
  • BGR vs RGB 是个容易忽略的坑,训练和推理必须对齐
  • 灰度图和二值图是形状分析的基础,简单但高效
  • HSV 适合做颜色分割,对光照变化鲁棒
  • Lab 适合做颜色差异度量,感知均匀

把这些基础打牢,后面讲图像增强、几何变换、滤波去噪的时候,你才能理解每一步操作到底在改变什么。