数字图像基础:从像素到色彩空间

图像处理的第一步,就是搞清楚图像到底是什么。说白了,计算机看到的不是「画面」,而是一堆数字。我刚开始接触这个领域时,总觉得图像处理很神秘,后来才发现——嗯,其实就是矩阵运算。

数字图像的表示方式

一张数字图像,本质上就是一个二维数组。每个位置上的数值,代表该点的亮度或颜色信息。

核心概念:图像 = f(x, y),其中 x、y 是空间坐标,f 是该点的像素值。

举个例子。一张 640×480 的图像,就是 640 列、480 行的矩阵。每个格子就是一个像素(pixel)。

图像类型 矩阵维度 每个像素的数值范围
二值图像 M × N 0 或 1
灰度图像 M × N 0 ~ 255
彩色图像 M × N × 3 每个通道 0 ~ 255

我在项目中遇到过一个问题:有人把灰度图像当彩色图像处理,结果程序直接崩溃。原因就是维度对不上。灰度图是二维矩阵,彩色图是三维张量,千万别搞混。

像素:图像的最小单元

像素就是图像里最小的「点」。每个像素有自己的坐标和数值。

  • 坐标:通常用 (行, 列) 表示,左上角为原点 (0, 0)
  • 值:灰度图里是亮度,彩色图里是颜色向量
  • 位深:每个像素用多少 bit 表示。8 bit 就是 0~255,16 bit 就是 0~65535

我的习惯:做阈值检测时,我一般先把图像转成 8 bit 灰度图。位深太高,计算量上去了,效果却不一定更好。

色彩空间:RGB、灰度与HSV

色彩空间,说白了就是描述颜色的「坐标系」。同一个颜色,在不同色彩空间里,数值完全不同。

RGB 色彩空间

最常用的色彩空间。红、绿、蓝三个通道叠加,组成所有颜色。

  • 每个通道 0~255
  • (0,0,0) 是黑色,(255,255,255) 是白色
  • 适合显示器、相机等硬件设备

但 RGB 有个问题:它跟人对颜色的感知不太匹配。你调一下亮度,三个通道都得改。这就很麻烦。

灰度图像

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

为什么阈值检测常用灰度图?因为简单、快。你想想看,处理一个通道比处理三个通道,计算量直接降到三分之一。

我曾经踩过的坑:直接对彩色图像做阈值分割,结果发现不同光照条件下,同一个物体的 RGB 值变化很大。后来改成先转灰度再处理,稳定性好多了。

HSV 色彩空间

HSV 把颜色拆成三个分量:色调(Hue)、饱和度(Saturation)、明度(Value)。

分量 含义 范围
H 色调,决定是什么颜色 0° ~ 180°(OpenCV 里)
S 饱和度,颜色纯度 0 ~ 255
V 明度,亮度 0 ~ 255

HSV 的好处是:色调和亮度是分开的。你想检测红色物体,直接看 H 通道就行,光照变化影响小。我个人做颜色相关的阈值检测时,首选 HSV。

色彩空间转换关系

下面这张图,是我自己总结的色彩空间转换逻辑。你看一遍,心里就有数了。

色彩空间转换关系图 RGB 色彩空间 灰度图像 HSV 色彩空间 转换说明 • RGB → 灰度:加权平均 Gray = 0.299R + 0.587G + 0.114B • RGB → HSV:非线性变换,分离色调与亮度 • HSV → RGB:逆变换,用于显示或存储 • 灰度 → RGB:复制到三个通道,得到伪彩色图

图像读取与显示:OpenCV 实战

理论说完了,咱们直接上手。Python 里最常用的图像库就是 OpenCV。我建议你装好 OpenCV 后,先跑通下面这段代码。

import cv2
import matplotlib.pyplot as plt

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

# 查看图像信息
print(f"图像尺寸: {img.shape}")
print(f"数据类型: {img.dtype}")
print(f"像素总数: {img.size}")

# 显示图像
cv2.imshow('Original Image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

注意:OpenCV 默认用 BGR 顺序读取彩色图像,不是 RGB。我第一次用的时候,显示出来的图像颜色全不对,查了半天才发现是这个原因。

色彩空间转换代码

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

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

# 显示转换结果
cv2.imshow('Gray', gray)
cv2.imshow('HSV', hsv)
cv2.waitKey(0)
cv2.destroyAllWindows()

你想想看,为什么 OpenCV 用 BGR 而不是 RGB?历史原因。早期相机硬件厂商用 BGR 排列,OpenCV 就沿用了这个习惯。所以显示图像时,记得用 cv2.COLOR_BGR2RGB 转一下。

像素操作:直接访问与修改

图像处理说到底,就是操作像素。下面这段代码,展示了如何直接访问和修改像素值。

# 访问像素 (行, 列)
pixel = img[100, 200]
print(f"BGR 值: {pixel}")

# 修改像素
img[100, 200] = [0, 0, 255]  # 变成红色

# 批量操作:将左上角 50x50 区域设为绿色
img[0:50, 0:50] = [0, 255, 0]

我曾经犯过的错:直接在大循环里逐像素操作,一张 1920×1080 的图像,处理一次要好几秒。后来改用 NumPy 的向量化操作,速度提升了上百倍。记住:能用矩阵运算就别用 for 循环。

图像数据类型与范围

数据类型 范围 常见用途
uint8 0 ~ 255 标准图像,最常用
uint16 0 ~ 65535 医学图像、RAW 格式
float32 0.0 ~ 1.0 深度学习输入、图像处理中间结果

做阈值检测时,我习惯先把图像转成 float32,归一化到 0~1 之间。这样后续的阈值参数更直观,也方便调试。

本章小结

  • 图像是矩阵,像素是矩阵里的元素
  • RGB 适合显示,HSV 适合颜色检测,灰度适合阈值分割
  • OpenCV 用 BGR 顺序,显示时记得转换
  • 操作像素优先用 NumPy 向量化,别写 for 循环