1. 计算机视觉基础:图像形成与表示、颜色空间、图像滤波与边缘检测
1.1 图像是怎么来的?——图像形成模型
做视觉这么多年,我经常跟新人说一句话:理解图像怎么来的,比理解怎么处理它更重要。你想想看,如果你连一张照片是怎么形成的都不清楚,后面那些滤波、检测、分割,说白了都是在瞎调参数。
图像形成,本质上是一个物理过程。光从光源出发,打到物体表面,反射后进入相机镜头,经过传感器采样,最终变成我们看到的像素矩阵。这里面有几个关键环节:
- 光源:自然光(太阳)或人造光(灯泡),决定了场景的亮度和色温
- 物体反射:不同材质反射率不同,金属反射强,布料反射弱
- 镜头成像:小孔成像模型的升级版,引入了畸变、景深等现实问题
- 传感器采样:CCD/CMOS把光信号转成电信号,再量化成数字值
核心公式(简化版):
像素值 = 光源强度 × 物体反射率 × 传感器灵敏度 + 噪声
嗯,这个公式虽然简单,但几乎所有图像处理算法都绕不开它。
我在项目中遇到过一件事:有一次做工业缺陷检测,白天晚上拍出来的图片亮度差异巨大,算法直接崩了。后来才发现,我们只考虑了物体反射率,完全没管光源变化。从那以后,我养成了一个习惯——做任何视觉项目,先搞清楚光照条件。
1.2 图像的数学表示
图像在计算机里怎么存的?说白了就是一个二维数组。灰度图是单通道,每个像素0-255;彩色图是三个通道(RGB),每个通道也是0-255。
举个例子,一张640×480的彩色图像,它的形状是(480, 640, 3)。480行,640列,3个颜色通道。你想想看,这其实就是把三维空间的信息压缩到了二维平面上。
# 用Python表示一张图像
import numpy as np
# 创建一个100x100的红色图像
height, width = 100, 100
red_image = np.zeros((height, width, 3), dtype=np.uint8)
red_image[:, :, 0] = 255 # R通道设为255
red_image[:, :, 1] = 0 # G通道设为0
red_image[:, :, 2] = 0 # B通道设为0
我的小建议:刚入门时,别急着调库。自己手动写一个图像读取、显示、保存的流程,哪怕只用OpenCV的几行代码,也能帮你建立「图像就是数组」这个直觉。
1.3 颜色空间——不只是RGB
RGB是最常见的颜色表示方式,但它不是万能的。我个人习惯把颜色空间分成三类:
| 颜色空间 | 特点 | 典型应用 |
|---|---|---|
| RGB | 直观,硬件友好 | 显示、存储 |
| HSV/HSL | 色调、饱和度、亮度分离 | 颜色分割、目标跟踪 |
| Lab | 感知均匀,与设备无关 | 颜色匹配、图像增强 |
为什么会需要这么多颜色空间?我举个例子。你在做交通灯检测时,用RGB去提取红色,光照一变就完蛋。但换成HSV,只看H(色调)通道,红色就是红色,不管它亮还是暗。这就是颜色空间选择的重要性。
我曾经踩过的坑:有一次做肤色检测,直接用RGB阈值,结果在黄光下全偏了。后来换成YCbCr空间,只取Cb和Cr两个色度通道,效果立马好了。记住:做颜色相关任务,先想想哪个空间最合适。
1.4 图像滤波——让图像变干净
滤波,说白了就是「邻居投票」。每个像素的值,由它周围像素共同决定。为什么要这么做?因为图像里有噪声,我们需要平滑掉那些「不和谐」的点。
常见的滤波方式有:
- 均值滤波:取邻域平均值,简单粗暴,但会模糊边缘
- 高斯滤波:加权平均,中心权重高,边缘权重低,效果更自然
- 中值滤波:取中位数,对椒盐噪声特别有效
- 双边滤波:同时考虑空间距离和像素差异,能保边去噪
# OpenCV中的滤波示例
import cv2
import numpy as np
img = cv2.imread('image.jpg')
# 高斯滤波
gaussian = cv2.GaussianBlur(img, (5, 5), 1.5)
# 中值滤波
median = cv2.medianBlur(img, 5)
# 双边滤波
bilateral = cv2.bilateralFilter(img, 9, 75, 75)
面试常问:高斯滤波的核大小和sigma怎么选?我的经验是:核大小取sigma的3倍左右,比如sigma=1.5,核大小取5×5。太小了去噪不够,太大了图像糊成一片。
1.5 边缘检测——找到图像的「骨架」
边缘是什么?说白了就是像素值发生剧烈变化的地方。你想想看,一张图像里最有信息量的部分,往往就是这些边缘——物体的轮廓、纹理的边界、光影的分界线。
边缘检测的核心思想是求导数。一阶导数的极大值对应边缘,二阶导数的过零点也对应边缘。常用的算子有:
- Sobel算子:一阶导数,分水平和垂直方向
- Laplacian算子:二阶导数,对噪声敏感
- Canny边缘检测:多阶段优化,目前最常用的方法
Canny算法是我个人最喜欢的边缘检测方法。它做了四件事:
- 高斯滤波去噪
- 计算梯度幅值和方向
- 非极大值抑制(只保留梯度方向上的局部最大值)
- 双阈值检测(高阈值确定强边缘,低阈值连接弱边缘)
# Canny边缘检测
edges = cv2.Canny(img, threshold1=50, threshold2=150)
# 参数调优建议
# threshold1和threshold2的比例一般在1:2到1:3之间
# 我习惯先试(50, 150),效果不好再调
避坑指南:我曾经在项目里直接用Canny的默认参数,结果边缘断断续续的。后来发现,先做一次高斯滤波,再调低threshold1的值,边缘就连起来了。记住:预处理比算法本身更重要。
1.6 本章小结
这一章的内容,说白了就是视觉算法的「地基」。图像形成让你知道数据从哪来,颜色空间让你知道怎么表示,滤波让你知道怎么去噪,边缘检测让你知道怎么提取特征。这四个知识点,几乎贯穿了所有计算机视觉任务。
我个人建议:别急着跑深度学习模型。先把这些基础操作手写一遍,用Python的numpy实现均值滤波、Sobel边缘检测,哪怕慢一点,也能帮你建立扎实的直觉。面试的时候,面试官问这些基础问题,你如果能从物理原理讲到工程实践,那基本就稳了。
下一章我们会聊特征提取与描述子,也就是怎么从图像里找到「关键点」。到时候再细聊。