2. 图像基础与色彩空间

各位同学,欢迎来到第二章。这一章我们聊点基础,但千万别小看它。我在工业现场摸爬滚打这些年,见过太多因为色彩空间搞错、分辨率没算对,导致整个检测项目翻车的案例。说白了,图像预处理的第一步,就是搞清楚你手里的图像到底是个什么东西。

你想想看,相机拍下来的那张图,在计算机眼里是什么?不是一张漂亮的照片,而是一堆数字。这一章,我们就来拆解这堆数字。

图像基础与色彩空间知识体系 数字图像基础 像素与分辨率 色彩空间 图像类型 像素坐标 分辨率计算 RGB HSV 灰度图 二值图 应用:图像读取 → 色彩空间转换 → 灰度化 → 二值化 → 特征提取

1. 像素:图像的最小单元

像素,英文叫 pixel,是 picture element 的缩写。一张图像,本质上就是一个二维像素矩阵。每个像素就是一个点,这个点有它的坐标 (x, y) 和颜色值。

我在项目中遇到过一件事:一个做 PCB 缺陷检测的同事,把 500 万像素的相机换成了 2000 万像素,结果检测精度没提升多少,处理时间却翻了三倍。为什么?因为他没搞清楚,像素数量翻倍,数据量可不是线性增长——是四倍。

核心概念:

  • 像素坐标:通常左上角为原点 (0,0),x 轴向右,y 轴向下
  • 像素值:灰度图是 0-255 的整数,彩色图是三个通道的向量
  • 像素深度:8 位、16 位、32 位浮点,决定了图像的精度

2. 分辨率:图像的精细程度

分辨率这个词,大家天天听。但工业视觉里的分辨率,和手机屏幕上的分辨率,侧重点不太一样。

工业上我们更关心的是 空间分辨率——也就是每个像素对应实际物理世界的多大尺寸。单位通常是 mm/pixel 或 μm/pixel。

分辨率类型 定义 工业应用示例
图像分辨率 图像的总像素数(宽×高) 1920×1080、2592×1944
空间分辨率 每个像素对应的物理尺寸 0.05 mm/pixel
检测精度 能检测到的最小缺陷尺寸 通常为 3-5 个像素

我的经验:选相机时,先算空间分辨率。比如你要检测 0.1mm 的划痕,按 3 个像素算,空间分辨率至少要 0.033 mm/pixel。再根据视场大小反推需要的相机像素数。这个公式我用了十年,从来没出过问题。

3. RGB 色彩空间

RGB 是最常见的色彩模型。红、绿、蓝三个通道,每个通道 0-255,组合起来就是 1600 多万种颜色。听起来很美好,对吧?

但在工业视觉里,RGB 有个大问题——它受光照影响太大了。你想想看,同一个工件,上午的阳光和下午的灯光下,RGB 值能差出好几十。我早期做手机外壳划痕检测时,就因为这个吃了大亏。

import cv2
import numpy as np

# 读取图像
img = cv2.imread('pcb_board.jpg')

# 分离 RGB 通道(OpenCV 默认是 BGR 顺序)
b, g, r = cv2.split(img)

# 查看某个像素的 RGB 值
pixel = img[100, 200]  # 第100行,第200列
print(f'BGR: {pixel}')  # 输出 [B, G, R]

注意:OpenCV 读取图像默认是 BGR 顺序,不是 RGB。很多新手在这里翻车。显示时要用 cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 转换。

4. HSV 色彩空间

HSV 是解决 RGB 光照敏感问题的利器。它把颜色分解成三个分量:

  • H(Hue,色调):0-180,代表颜色的种类,比如红色、蓝色
  • S(Saturation,饱和度):0-255,颜色的纯度
  • V(Value,明度):0-255,颜色的亮度

为什么 HSV 在工业里好用?因为 H 通道对光照变化不敏感。你只需要在 H 通道上设阈值,就能稳定地分割出目标区域。

我记得有一次做药片颜色分拣,不同批次的药片因为包衣厚度不同,RGB 值差异很大。但转到 HSV 后,H 值几乎不变。用 H 通道做阈值分割,准确率从 85% 直接干到了 99.5%。

import cv2
import numpy as np

# 读取图像
img = cv2.imread('pill.jpg')

# BGR 转 HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 提取 H 通道
h = hsv[:, :, 0]

# 设定红色范围(OpenCV 中红色 H 值在 0-10 和 170-180)
lower_red = np.array([0, 50, 50])
upper_red = np.array([10, 255, 255])
mask1 = cv2.inRange(hsv, lower_red, upper_red)

lower_red2 = np.array([170, 50, 50])
upper_red2 = np.array([180, 255, 255])
mask2 = cv2.inRange(hsv, lower_red2, upper_red2)

# 合并两个红色区域
mask = cv2.bitwise_or(mask1, mask2)

避坑指南:OpenCV 中 H 的范围是 0-180,不是 0-360。这是为了用 uint8 存储。另外,红色的 H 值在 0 和 180 两端,需要做区间合并。我曾经因为这个 bug 排查了一整个下午。

5. 灰度图

灰度图,就是只有亮度信息,没有颜色信息的图像。每个像素一个值,0 是纯黑,255 是纯白。

为什么工业视觉里经常用灰度图?两个原因:

  1. 数据量小:一张灰度图只有彩色图的 1/3 大小
  2. 算法简单:很多检测算法(边缘检测、模板匹配)在灰度图上跑得更快、更稳定

灰度化的方法有好几种,最常用的是加权平均法:

import cv2

# 读取彩色图
img = cv2.imread('gear.jpg')

# 转灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 加权公式:Gray = 0.299*R + 0.587*G + 0.114*B
# 这个权重是根据人眼对绿色最敏感设计的

工业场景建议:如果检测对象颜色信息不重要(比如尺寸测量、缺陷检测),直接用灰度图。我做过一个轴承外径测量项目,灰度图处理速度比彩色图快 3 倍,精度完全一样。

6. 二值图

二值图,就是只有黑白两种颜色的图像。每个像素要么是 0(黑),要么是 255(白)。

二值化是图像分割的基石。怎么做?设定一个阈值,大于阈值的变白,小于阈值的变黑。就这么简单。

但阈值怎么选?这就讲究了。

二值化方法 原理 适用场景
全局阈值 整个图像用一个固定阈值 光照均匀、背景单一
Otsu 阈值 自动计算最佳阈值 双峰直方图
自适应阈值 每个像素用局部邻域计算阈值 光照不均匀
import cv2

# 读取灰度图
gray = cv2.imread('gear.jpg', cv2.IMREAD_GRAYSCALE)

# 全局阈值
_, binary_global = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)

# Otsu 阈值
_, binary_otsu = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# 自适应阈值
binary_adaptive = cv2.adaptiveThreshold(
    gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
    cv2.THRESH_BINARY, 11, 2
)

我曾经踩过的坑:做金属表面缺陷检测时,工件表面有反光,全局阈值死活分不开。后来换成自适应阈值,配合高斯权重,效果立竿见影。记住:工业现场的光照永远不是理想均匀的。

7. 色彩空间转换实战

最后,我们把这些知识串起来。一个典型的工业视觉预处理流程是这样的:

  1. 读取图像(BGR)
  2. 转 HSV 做颜色分割
  3. 转灰度图做边缘检测
  4. 二值化提取目标区域
  5. 形态学操作去噪
import cv2
import numpy as np

def preprocess_for_defect_detection(image_path):
    # 1. 读取图像
    img = cv2.imread(image_path)
    
    # 2. 转 HSV,提取感兴趣颜色区域
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    lower = np.array([35, 50, 50])   # 绿色范围
    upper = np.array([85, 255, 255])
    mask_color = cv2.inRange(hsv, lower, upper)
    
    # 3. 转灰度
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # 4. 二值化
    _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    
    # 5. 合并掩码
    result = cv2.bitwise_and(binary, binary, mask=mask_color)
    
    return result

嗯,这一章的内容就到这里。像素、分辨率、RGB、HSV、灰度图、二值图——这些概念看起来简单,但每一个都是工业视觉的基石。你想想看,如果连图像的基本构成都不清楚,后面的目标检测、缺陷识别又从何谈起?