4. 计算机视觉基础(下):图像特征提取与几何变换

好,咱们接着往下聊。上一节我们把图像预处理和滤波讲透了,这一节要聊的,是真正让计算机「看懂」图像的关键——特征提取,以及图像在空间中的变换。说白了,特征就是图像的「指纹」,而几何变换就是让图像「动起来」的手段。

4.1 图像特征提取:HOG 与 LBP

特征提取这件事,我个人的理解是:把一张几千几万像素的图,浓缩成几百个有意义的数字。你想想看,直接拿像素去比,光照一变、角度一变,结果就全乱了。所以我们需要更鲁棒的特征。

4.1.1 HOG(方向梯度直方图)

HOG 在行人检测领域可是个传奇。2005 年 Dalal 和 Triggs 提出后,直到深度学习兴起前,它一直是主流方案。它的核心思想很简单:物体的形状和外观,可以通过局部梯度方向的分布来描述。

HOG 提取流程:

  1. 图像预处理:灰度化 + Gamma 校正(降低光照影响)
  2. 计算梯度:用 Sobel 算子算水平和垂直梯度
  3. 构建 Cell 直方图:每个 8×8 像素块为一个 cell,统计 9 个方向的梯度
  4. Block 归一化:2×2 个 cell 组成一个 block,做局部对比度归一化
  5. 串联特征:把所有 block 的特征向量拼起来

关键参数:一个 64×128 的行人图像,cell 大小 8×8,block 大小 16×16,步长 8 像素,最终特征维度 = 7×15×36 = 3780 维。

# OpenCV 中提取 HOG 特征的示例
import cv2
import numpy as np

# 初始化 HOG 描述子
hog = cv2.HOGDescriptor()
# 设置 SVM 检测器为默认行人检测器
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())

# 读取图像并检测
img = cv2.imread('pedestrian.jpg')
# 调整图像大小到 64×128
img_resized = cv2.resize(img, (64, 128))
features = hog.compute(img_resized)
print(f'HOG 特征维度: {features.shape}')  # 输出: (3780, 1)

我的经验:我在做安防项目时,发现 HOG 对光照变化确实鲁棒,但遇到遮挡严重的场景就抓瞎了。后来我加了一个滑动窗口 + 多尺度检测,效果好了不少。嗯,这里要注意:HOG 的计算量不小,实时性要求高的场景要慎重。

4.1.2 LBP(局部二值模式)

LBP 是另一种经典特征,特别擅长描述纹理。它的思路更直接:比较中心像素和周围像素的大小关系。

LBP 的计算步骤:

  1. 取 3×3 邻域,中心像素为阈值
  2. 周围 8 个像素与阈值比较,大于等于为 1,小于为 0
  3. 按顺时针方向得到一个 8 位二进制数(0-255)
  4. 统计所有像素的 LBP 值,得到直方图作为特征
# 手动实现 LBP 特征提取
def lbp_feature(image):
    h, w = image.shape
    lbp_image = np.zeros((h-2, w-2), dtype=np.uint8)
    
    # 定义邻域偏移
    offsets = [(-1,-1), (-1,0), (-1,1), (0,1), 
               (1,1), (1,0), (1,-1), (0,-1)]
    
    for i in range(1, h-1):
        for j in range(1, w-1):
            center = image[i, j]
            binary = 0
            for k, (dx, dy) in enumerate(offsets):
                binary |= (image[i+dx, j+dy] >= center) << k
            lbp_image[i-1, j-1] = binary
    
    # 计算直方图
    hist = cv2.calcHist([lbp_image], [0], None, [256], [0, 256])
    # 归一化
    hist = cv2.normalize(hist, hist).flatten()
    return hist

避坑指南:我曾经在纹理相似度匹配任务中直接用原始 LBP,结果效果很差。后来发现是因为没有使用「旋转不变 LBP」和「均匀模式 LBP」。对于旋转变化大的场景,一定要用圆形邻域 + 旋转不变模式。

4.2 图像金字塔与缩放

为什么要聊图像金字塔?因为在目标检测中,物体的大小是不确定的。一个行人可能在近处占满屏幕,也可能在远处只有几十个像素。图像金字塔就是解决这个问题的经典方案。

4.2.1 高斯金字塔

高斯金字塔由一系列逐渐缩小的图像组成。每一层都是对上一层进行高斯模糊后下采样得到的。

# 构建高斯金字塔
def build_gaussian_pyramid(image, levels=4):
    pyramid = [image]
    for i in range(levels - 1):
        # 高斯模糊 + 下采样
        blurred = cv2.GaussianBlur(pyramid[-1], (5, 5), 1.5)
        down = cv2.resize(blurred, (blurred.shape[1]//2, blurred.shape[0]//2))
        pyramid.append(down)
    return pyramid

# 使用示例
img = cv2.imread('scene.jpg', 0)
pyramid = build_gaussian_pyramid(img, 5)
for i, layer in enumerate(pyramid):
    print(f'第 {i+1} 层: {layer.shape}')

4.2.2 拉普拉斯金字塔

拉普拉斯金字塔记录的是高斯金字塔相邻层之间的差异。它常用于图像压缩和重建。

实际应用:在行人检测中,我们通常对原始图像构建 4-8 层金字塔,然后在每一层上用固定大小的检测窗口滑动。这样就能检测到不同大小的目标。我习惯把缩放因子设为 1.05 或 1.1,太大会漏检,太小计算量爆炸。

4.3 图像几何变换

几何变换说白了就是让图像「变形」。在目标跟踪中,我们需要预测目标在下一帧的位置和姿态,这就离不开几何变换。

4.3.1 仿射变换

仿射变换保持图像的「平行性」——直线还是直线,平行线还是平行线。它由平移、旋转、缩放、剪切组成。

# 仿射变换示例
import cv2
import numpy as np

img = cv2.imread('car.jpg')
h, w = img.shape[:2]

# 定义三个源点和目标点
src_points = np.float32([[50, 50], [200, 50], [50, 200]])
dst_points = np.float32([[10, 100], [200, 50], [100, 250]])

# 计算仿射变换矩阵
M = cv2.getAffineTransform(src_points, dst_points)

# 应用变换
transformed = cv2.warpAffine(img, M, (w, h))

我的经验:在车辆跟踪项目中,我常用仿射变换来校正视角变化。比如车辆从远处驶近时,用仿射变换可以把它「拉」回标准视角,这样特征匹配更稳定。但要注意:仿射变换只有 6 个自由度,对于透视变形严重的场景不够用。

4.3.2 透视变换

透视变换(也叫投影变换)有 8 个自由度,可以处理更复杂的变形。它不保持平行性,但能模拟真实的透视效果。

# 透视变换示例
# 定义四个角点(比如矫正一张倾斜拍摄的文档)
src_pts = np.float32([[0, 0], [w-1, 0], [w-1, h-1], [0, h-1]])
dst_pts = np.float32([[50, 50], [w-50, 30], [w-30, h-20], [40, h-40]])

# 计算透视变换矩阵
M = cv2.getPerspectiveTransform(src_pts, dst_pts)

# 应用变换
perspective = cv2.warpPerspective(img, M, (w, h))

避坑指南:我曾经在做车道线检测时,直接用透视变换把路面投影成鸟瞰图。结果发现变换后的图像边缘出现了严重的锯齿和空洞。后来我意识到:透视变换后一定要用合适的插值方法(比如双线性插值),并且要处理好边界像素。

4.4 综合实战:特征提取 + 几何变换

最后,我把这些知识串起来,给一个实际场景中的应用思路。

场景 特征选择 几何变换 我的建议
行人检测 HOG 图像金字塔 多尺度检测,缩放因子 1.05
纹理识别 LBP 仿射变换 使用旋转不变 LBP
车辆跟踪 HOG + LBP 融合 透视变换 先矫正视角再提取特征
文档矫正 边缘特征 透视变换 先检测文档四个角点

好了,这一节的内容就到这里。说白了,特征提取和几何变换是计算机视觉的两大基石。你想想看,没有特征,计算机就是「盲人摸象」;没有几何变换,目标一变形就认不出来了。下一节我们会把这些技术用到实际的目标检测框架中,到时候你就知道这些基础有多重要了。

核心要点回顾:

  • HOG 适合描述形状,LBP 适合描述纹理
  • 图像金字塔解决多尺度问题,缩放因子建议 1.05-1.1
  • 仿射变换保持平行性,透视变换更灵活
  • 实际项目中,特征提取和几何变换往往要配合使用