3、图像处理基础(一):色彩空间转换与图像滤波
各位同学,今天我们正式进入图像处理的核心环节。说实话,很多初学者一上来就调模型、跑深度学习,结果连最基本的色彩空间和滤波都搞不清楚。我见过太多项目翻车,就是因为预处理没做好。今天咱们就把这两个基础打扎实。
本章核心脉络:色彩空间转换(RGB→HSV→灰度)→ 图像滤波(均值、高斯、中值)→ 实战避坑指南
3.1 色彩空间转换:从RGB说起
RGB 是我们最熟悉的色彩模型。红绿蓝三个通道,每个 0-255。但说实话,RGB 并不适合做图像分析。为什么?因为 RGB 的三个通道高度相关,光照一变,三个值一起变,你很难单独提取颜色信息。
我个人习惯,做颜色检测时首选 HSV。H 是色调,S 是饱和度,V 是明度。你看,色调和光照基本解耦了。我在项目中遇到过一个问题:用 RGB 做红色物体检测,白天好好的,傍晚光线一暗就全丢了。换成 HSV 后,只判断 H 通道范围,稳得很。
我的经验:HSV 中 H 的取值范围是 0-179(OpenCV 里),不是 0-359。很多新手在这里踩坑,一上来写 360,结果颜色全不对。
灰度图就更简单了。说白了就是把彩色信息丢掉,只保留亮度。公式是:Gray = 0.299*R + 0.587*G + 0.114*B。为什么权重不一样?因为人眼对绿色最敏感,对蓝色最不敏感。
import cv2
import numpy as np
# 读取图像
img = cv2.imread('example.jpg')
# RGB 转 HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# RGB 转灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 查看形状
print('原图:', img.shape) # (H, W, 3)
print('HSV:', hsv.shape) # (H, W, 3)
print('灰度:', gray.shape) # (H, W)
注意:OpenCV 默认是 BGR 顺序,不是 RGB。用 cv2.imread() 读进来的图像,通道顺序是 B、G、R。转 HSV 时要用 COLOR_BGR2HSV,不是 COLOR_RGB2HSV。我曾经因为这个 bug 排查了整整一下午。
3.2 图像滤波:去噪是门手艺
滤波说白了就是「用周围像素的信息来修正当前像素」。你想想看,如果图像里有噪点,一个白点孤零零地出现在黑背景上,那肯定不正常。滤波就是把这些异常点抹平。
3.2.1 均值滤波
最简单粗暴的方法。取一个窗口(比如 3x3),计算窗口内所有像素的平均值,替换中心像素。代码就一行:
# 均值滤波,核大小为 5x5
blurred = cv2.blur(img, (5, 5))
但均值滤波有个问题:它会把边缘也模糊掉。边缘处的像素值变化剧烈,一平均,边缘就糊了。所以均值滤波适合去除均匀的噪声,但不适合保留细节的场景。
3.2.2 高斯滤波
高斯滤波比均值滤波聪明一点。它给窗口内的像素分配不同的权重——离中心越近,权重越大。权重分布服从高斯函数。这样既能去噪,又能更好地保留边缘。
# 高斯滤波,核大小 5x5,标准差 1.5
gaussian = cv2.GaussianBlur(img, (5, 5), 1.5)
这里有个参数要留意:核大小必须是奇数。为什么?因为奇数才有中心像素。3x3、5x5、7x7 都行,但 4x4 会报错。我刚开始学的时候就被这个坑过。
核大小 vs 标准差:核越大,图像越模糊。标准差越大,权重分布越平坦,模糊效果也越强。一般建议核大小取 5x5,标准差取 1.0-2.0 之间。
3.2.3 中值滤波
中值滤波是我个人最喜欢的去噪方式,尤其是对付椒盐噪声(就是那种黑白点噪声)。它取窗口内所有像素的中位数,而不是平均值。这样有个好处:如果一个白点周围全是黑点,中位数肯定是黑,白点就被干掉了。
# 中值滤波,核大小为 5
median = cv2.medianBlur(img, 5)
注意这里核大小直接传一个整数,不是元组。而且核越大,去噪越强,但图像也会变模糊。对于椒盐噪声,3x3 或 5x5 就够用了。
3.3 三种滤波对比
| 滤波类型 | 原理 | 适用场景 | 边缘保留 | 速度 |
|---|---|---|---|---|
| 均值滤波 | 窗口内像素平均 | 均匀噪声 | 差 | 快 |
| 高斯滤波 | 加权平均(高斯权重) | 高斯噪声、通用去噪 | 中等 | 中等 |
| 中值滤波 | 窗口内像素中位数 | 椒盐噪声 | 好 | 较慢 |
我的选择策略:如果图像里有明显的黑白噪点,优先用中值滤波。如果是传感器噪声(均匀分布),用高斯滤波。均值滤波我基本不用,除非对速度要求极高且不在乎边缘模糊。
3.4 实战避坑指南
嗯,这里我要多说几句。图像预处理看起来简单,但坑真的不少。
- 数据类型问题:OpenCV 读进来是 uint8,范围 0-255。滤波后还是 uint8。但如果你做数学运算,比如两个图像相减,结果可能变成负数,uint8 会截断。我建议先转成 float32 再处理。
- 边界处理:滤波时,图像边缘的像素怎么办?OpenCV 默认是补零,但这样边缘会有黑边。可以用 BORDER_REFLECT 或 BORDER_REPLICATE 来改善。
- 彩色图像滤波:对彩色图像滤波时,OpenCV 会自动对每个通道分别处理。但中值滤波对彩色图像效果一般,因为三个通道的中位数可能来自不同像素,导致颜色失真。我一般先把彩色图转成灰度,或者对每个通道单独滤波。
# 推荐做法:先转 float32 再滤波
img_float = img.astype(np.float32) / 255.0
blurred = cv2.GaussianBlur(img_float, (5, 5), 1.5)
# 处理完再转回 uint8
result = (blurred * 255).astype(np.uint8)
曾经踩过的坑:有一次做车牌识别,图像里有大量椒盐噪声。我用高斯滤波处理,结果车牌字符边缘全糊了,识别率掉到 30%。换成中值滤波后,识别率直接回到 95%。所以选对滤波方法,比调参重要得多。
好了,今天的内容就到这里。色彩空间转换和图像滤波是图像处理的基石,看似简单,但用好了能解决 80% 的预处理问题。下一章我们会继续深入,讲直方图均衡化和形态学操作,到时候你会看到这些基础操作如何组合出强大的效果。
公众号:蓝海资料掘金营,微信deep3321