第二章 图像处理基础(上):像素与色彩空间、图像滤波、边缘检测
各位同学,欢迎来到第二章。这一章咱们要啃的,是视觉算法的地基——图像处理基础。说白了,就是搞明白计算机到底是怎么“看”一张图片的。
我个人习惯把图像处理比作“做饭”。像素就是食材,色彩空间就是菜系(川菜、粤菜),滤波就是刀工和火候,边缘检测就是最后摆盘时勾勒出的轮廓线。你想想看,如果连食材和刀工都搞不明白,后面的大菜(目标检测、分割)根本没法做。
2.1 像素:图像的最小单元
一张数字图像,本质上就是一个二维矩阵。矩阵里的每个元素,就是一个像素。每个像素存储着颜色信息。
举个例子,一张 640×480 的图片,就是 640 列、480 行的像素点阵。每个点都有自己的颜色值。
核心概念:
- 分辨率:图像中像素的总数。比如 1920×1080,约 200 万像素。
- 位深:每个像素用多少位(bit)来表示。8 位灰度图,每个像素值范围 0~255。
- 通道数:灰度图是单通道,彩色图通常是三通道(RGB)。
我在项目中遇到过一个问题:客户给的图片明明是彩色的,但算法跑出来全是黑白。后来发现是 OpenCV 默认用 BGR 顺序读取,而我直接用 RGB 去处理了。嗯,这里要注意,OpenCV 的通道顺序是 BGR,不是 RGB。
2.2 色彩空间:计算机眼中的颜色
色彩空间,就是描述颜色的数学模型。不同的色彩空间,适用于不同的场景。
2.2.1 RGB 色彩空间
RGB 是最常见的色彩空间。它用红(R)、绿(G)、蓝(B)三个通道的叠加来表示颜色。每个通道 0~255,三个通道组合起来就是 1600 多万种颜色。
但 RGB 有个缺点:它和人眼感知颜色的方式不太一样。你想想看,调整亮度时,R、G、B 三个值都要改,很麻烦。
2.2.2 HSV 色彩空间
HSV 就聪明多了。它把颜色分解成三个分量:
- H(Hue,色调):颜色的种类,比如红色、绿色。范围 0~180。
- S(Saturation,饱和度):颜色的纯度。0 是灰色,255 是最鲜艳。
- V(Value,明度):颜色的明亮程度。0 是黑色,255 是白色。
我建议在做颜色识别时,优先用 HSV。比如识别红色交通灯,只需要设定 H 值在 0~10 和 170~180 之间,S 和 V 适当过滤一下,效果比 RGB 好得多。
避坑指南:我曾经在 HSV 范围设定上吃过亏。OpenCV 中 H 的范围是 0~180,不是 0~360。如果你用其他库的习惯去设 H 值,结果会完全不对。
2.2.3 灰度图
灰度图就是单通道图像,每个像素只有一个亮度值。0 是纯黑,255 是纯白。
为什么需要灰度图?因为很多算法(比如边缘检测、特征提取)在灰度图上计算量更小,而且不受颜色干扰。我一般拿到彩色图后,第一步就是转灰度。
import cv2
# 读取彩色图
img = cv2.imread('image.jpg')
# 转灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 转 HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
2.3 图像滤波:去噪与平滑
滤波,说白了就是“模糊”图像。为什么要模糊?因为真实世界的图像都有噪声,比如摄像头传感器带来的椒盐噪声、高斯噪声。滤波就是为了去掉这些噪声,让后续处理更稳定。
2.3.1 均值滤波
均值滤波是最简单的。它用一个窗口(比如 3×3)在图像上滑动,窗口内所有像素的平均值,就是中心像素的新值。
优点是速度快。缺点是会模糊边缘,而且对椒盐噪声效果一般。
2.3.2 高斯滤波
高斯滤波比均值滤波更“聪明”。它给窗口内的像素分配不同的权重——离中心越近,权重越大。权重分布符合高斯函数。
我个人习惯用高斯滤波。它在去噪和保留边缘之间取得了不错的平衡。尤其是处理高斯噪声时,效果很好。
2.3.3 中值滤波
中值滤波用窗口内所有像素的中位数,代替中心像素的值。
它对椒盐噪声(黑白点噪声)有奇效。我在项目中遇到过摄像头有坏点的情况,用中值滤波一下就搞定了。
注意:中值滤波的计算量比均值和高斯都大。如果图像很大,要考虑性能问题。
import cv2
img = cv2.imread('noisy_image.jpg')
# 均值滤波
blur_avg = cv2.blur(img, (5, 5))
# 高斯滤波
blur_gauss = cv2.GaussianBlur(img, (5, 5), 1.5)
# 中值滤波
blur_median = cv2.medianBlur(img, 5)
2.4 边缘检测:找到物体的轮廓
边缘,就是图像中像素值发生剧烈变化的地方。边缘检测的目标,就是把这些变化点找出来。
2.4.1 Sobel 算子
Sobel 算子是一种基于梯度的边缘检测方法。它用两个 3×3 的卷积核,分别计算水平方向和垂直方向的梯度。
水平梯度 Gx 检测垂直边缘,垂直梯度 Gy 检测水平边缘。最终的边缘强度是两者的组合。
我建议在需要方向信息时用 Sobel。比如检测车道线,水平边缘比垂直边缘更有用。
2.4.2 Canny 边缘检测
Canny 是目前最流行的边缘检测算法。它比 Sobel 复杂,但效果也更好。它的流程是:
- 高斯滤波:去噪。
- 计算梯度:用 Sobel 算梯度幅值和方向。
- 非极大值抑制:只保留梯度方向上的局部最大值,让边缘变细。
- 双阈值检测:设定高低两个阈值。高于高阈值的,是强边缘;介于两者之间的,是弱边缘;低于低阈值的,丢弃。
- 边缘连接:只保留与强边缘相连的弱边缘。
双阈值是 Canny 的精髓。我一般设高阈值为 150,低阈值为 50。但具体数值要根据图像调整。
避坑指南:我曾经在 Canny 的阈值上踩过坑。如果高阈值设得太高,会丢失很多真实边缘;设得太低,又会引入大量噪声。建议先用直方图观察梯度分布,再设定阈值。
import cv2
img = cv2.imread('image.jpg', 0) # 直接读成灰度图
# Sobel 边缘检测
sobel_x = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
sobel_y = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
sobel = cv2.magnitude(sobel_x, sobel_y)
# Canny 边缘检测
edges = cv2.Canny(img, 50, 150)
2.5 本章知识体系
下面这张图,把本章的核心逻辑串起来了。从像素出发,经过色彩空间转换,再到滤波去噪,最后到边缘检测。每一步都是下一步的基础。
好了,这一章的内容就到这里。像素、色彩空间、滤波、边缘检测,这四个概念是图像处理的基石。你想想看,后面要学的特征提取、目标检测,哪个不是建立在这些基础之上?
记得多动手写代码。光看不练,等于白学。
公众号:蓝海资料掘金营,微信deep3321