3. 图像基础与色彩空间:像素、分辨率与颜色模型
做工业视觉这几年,我最大的感触就是:图像的本质,其实就是一堆数字。你想想看,相机拍到的每一张图,在计算机眼里不过是一个巨大的矩阵。今天这一章,我们就来彻底搞懂这些数字到底代表什么,以及怎么在RGB、HSV、GRAY这些色彩空间之间来回切换。
3.1 像素与分辨率:图像的“原子”和“画布尺寸”
像素(Pixel)是图像的最小单元。每个像素就是一个“小方块”,里面存着颜色信息。分辨率呢,就是这张图有多少行、多少列像素。比如1920×1080,意思是有1920列、1080行。
核心公式:
- 总像素数 = 宽 × 高(单位:像素)
- 图像尺寸(字节) = 宽 × 高 × 通道数(单位:字节)
- 物理尺寸(mm) = 像素数 × 像元尺寸(μm/像素)
我在项目中遇到过一个问题:客户说他们的产品尺寸是10mm,要求检测精度0.05mm。我算了一下,10 / 0.05 = 200,理论上200像素就够了。但实际我选了500万像素的相机。为什么?因为还要考虑边缘模糊、光照不均匀、算法鲁棒性。说白了,理论值只是下限,工程上至少要留2~3倍余量。
| 分辨率 | 总像素数 | 典型应用场景 |
|---|---|---|
| 640×480 (VGA) | 30万 | 低端定位、条码识别 |
| 1280×1024 | 130万 | 通用检测、尺寸测量 |
| 1920×1080 (FHD) | 200万 | 表面缺陷检测 |
| 2448×2048 | 500万 | 高精度测量、小缺陷检测 |
| 4096×2160 | 880万 | 大视野高精度、晶圆检测 |
避坑指南:我曾经在一个项目中选了2000万像素的相机,结果发现处理速度根本跟不上。分辨率越高,数据量越大,处理时间越长。工业现场往往要求实时性,不要盲目追求高分辨率,够用就好。
3.2 色彩空间:RGB、HSV、GRAY
色彩空间,说白了就是“用哪套规则来描述颜色”。不同的任务,适合不同的规则。
3.2.1 GRAY(灰度图)
灰度图每个像素只有一个值,范围0~255。0是纯黑,255是纯白。工业视觉中,大部分算法第一步就是把彩色图转成灰度图。为什么?因为数据量直接减少到1/3,而且很多特征(边缘、纹理)在灰度图上就足够明显了。
转换公式很简单:
Gray = 0.299 × R + 0.587 × G + 0.114 × B
注意这个权重。人眼对绿色最敏感,对蓝色最不敏感。所以绿色通道权重最高。我见过有人直接用(R+G+B)/3做平均,结果图像看起来灰蒙蒙的,细节丢失严重。嗯,这里要注意:不要偷懒用平均法,除非你明确知道自己在做什么。
3.2.2 RGB(红绿蓝)
RGB是最常见的色彩空间。每个像素3个通道,每个通道0~255。显示器、相机传感器,基本都是RGB的。
但RGB有个问题:它不适合做颜色识别。为什么?因为RGB的三个通道高度相关。光照一变,R、G、B三个值一起变,你很难判断到底是颜色变了还是亮度变了。
我的经验:在工业现场做颜色分类时,我几乎不用RGB。光照稍微偏一点,同一个红色在RGB空间里可能差出50个数值。这时候就需要HSV出场了。
3.2.3 HSV(色相、饱和度、明度)
HSV把颜色拆成三个更“直观”的分量:
- H(Hue,色相):0°~360°,代表是什么颜色。红色0°,绿色120°,蓝色240°。
- S(Saturation,饱和度):0~1,代表颜色有多纯。0是灰色,1是纯色。
- V(Value,明度):0~1,代表亮度。0是黑色,1是最亮。
HSV最大的好处是:H通道对光照变化不敏感。你只需要设定一个H的范围,就能稳定地识别某种颜色。我在做PCB板上的电阻色环识别时,就是靠HSV的H通道来区分不同阻值的。
RGB转HSV的代码实现:
import cv2
import numpy as np
# 读取图像
img_bgr = cv2.imread('sample.jpg')
# BGR转HSV(OpenCV默认是BGR顺序)
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)
# 提取H通道
h_channel = img_hsv[:, :, 0]
# 设定红色范围(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(img_hsv, lower_red1, upper_red1)
mask2 = cv2.inRange(img_hsv, lower_red2, upper_red2)
mask = cv2.bitwise_or(mask1, mask2)
# 提取红色区域
result = cv2.bitwise_and(img_bgr, img_bgr, mask=mask)
注意:OpenCV中HSV的H范围是0~180(不是0~360),S和V是0~255(不是0~1)。这是为了用uint8存储。很多新手在这里翻车,我当年也踩过这个坑。
3.3 直方图分析:图像的“统计报告”
直方图就是统计每个像素值出现了多少次。横轴是像素值(0~255),纵轴是出现次数。一张图好不好,看直方图就能知道个大概。
3.3.1 直方图能告诉我们什么?
- 曝光是否正常:像素集中在左侧(暗部)说明欠曝,集中在右侧(亮部)说明过曝。
- 对比度高低:直方图分布越宽,对比度越高;越窄,图像越“灰”。
- 是否有过曝/欠曝区域:如果0或255处有很高的峰值,说明有大量纯黑或纯白区域,细节丢失了。
绘制直方图的代码:
import cv2
import matplotlib.pyplot as plt
# 读取灰度图
img_gray = cv2.imread('sample.jpg', cv2.IMREAD_GRAYSCALE)
# 计算直方图
hist = cv2.calcHist([img_gray], [0], None, [256], [0, 256])
# 绘制
plt.figure(figsize=(10, 4))
plt.plot(hist, color='black')
plt.title('Grayscale Histogram')
plt.xlabel('Pixel Value')
plt.ylabel('Frequency')
plt.grid(True)
plt.show()
3.3.2 直方图均衡化:让图像“舒展”开
如果直方图集中在一个小范围,图像看起来就灰蒙蒙的。直方图均衡化就是把这个分布“拉平”,让像素值均匀分布在整个0~255区间。
# 直方图均衡化
img_equalized = cv2.equalizeHist(img_gray)
# 对比效果
cv2.imshow('Original', img_gray)
cv2.imshow('Equalized', img_equalized)
cv2.waitKey(0)
实战技巧:在工业检测中,如果光照不均匀,可以先做直方图均衡化再处理。但要注意:均衡化会改变图像的亮度关系。如果你要做精确的灰度测量(比如判断某个区域是否达到特定灰度值),就不要用均衡化。
3.4 知识体系总览
下面这张图把本章的核心知识点串起来了。你可以把它当作一个快速索引:
3.5 本章小结
这一章我们聊了三件事:
- 像素与分辨率:搞清楚图像的基本单位,学会根据检测精度反推需要的分辨率。
- 色彩空间转换:GRAY省数据、RGB适合显示、HSV适合颜色识别。选对色彩空间,事半功倍。
- 直方图分析:用统计的眼光看图像,快速判断曝光和对比度问题。
说实话,这些基础概念看起来简单,但我在实际项目中见过太多因为色彩空间选错、分辨率算错而翻车的案例。嗯,基础打牢了,后面讲图像预处理和模型训练的时候,你才能游刃有余。