4. 图像预处理(上):图像缩放、裁剪、旋转与仿射变换、图像滤波、图像增强

各位同学,欢迎来到第四章。从这章开始,我们正式进入「动手干活」的阶段。

图像预处理,说白了就是给原始图像「洗个澡、理个发、化个妆」。你从摄像头、手机、网上下载的图片,很少能直接喂给算法的。为什么?因为原始数据太「脏」了——尺寸不对、角度歪了、噪声太多、亮度不均。我这些年踩过的坑,十有八九都跟预处理没做好有关。

好,咱们一个一个来。

4.1 图像缩放与裁剪

先说最基础的。你训练一个分类网络,输入尺寸是 224x224,但你的图片可能是 1920x1080。怎么办?缩呗。

4.1.1 图像缩放

缩放的核心是插值。说白了,就是新像素点的值怎么算。

  • 最近邻插值:最快,但锯齿严重。适合像素风格的游戏,不适合正经算法。
  • 双线性插值:最常用。取周围4个点加权平均。效果不错,速度也快。
  • 双三次插值:取周围16个点。更平滑,但慢。我一般只在放大图片时用。

我的经验:做目标检测时,我习惯用双线性插值。又快又好。做超分辨率时,才上双三次。

import cv2

img = cv2.imread('input.jpg')
# 缩放到 224x224
resized = cv2.resize(img, (224, 224), interpolation=cv2.INTER_LINEAR)

小技巧:保持宽高比缩放时,先算比例,再resize。不然人脸会变形成外星人。

4.1.2 图像裁剪

裁剪就简单了。就是取图像的一个矩形区域。

但要注意:裁剪会丢失信息。你想想看,如果目标刚好在裁剪边界上,那就尴尬了。

# 裁剪 [y1:y2, x1:x2]
cropped = img[100:300, 200:400]

注意:OpenCV中坐标顺序是 (y, x) 不是 (x, y)。我刚开始学的时候被这个坑过好几次。

4.2 旋转与仿射变换

旋转和仿射变换,是图像几何变换的核心。说白了,就是让图像「动起来」——转个角度、拉个透视、做个矫正。

4.2.1 图像旋转

旋转很简单。但有个坑:旋转后图像会超出边界。OpenCV默认会裁剪掉超出部分。如果你不想丢失信息,可以设置 cv2.BORDER_WRAP 或手动调整画布大小。

h, w = img.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, 45, 1.0)  # 旋转45度,不缩放
rotated = cv2.warpAffine(img, M, (w, h))

避坑指南:我曾经做车牌识别时,直接旋转了整张图,结果车牌倒是正了,但背景全黑了。后来我改成只旋转车牌区域,效果好多了。

4.2.2 仿射变换

仿射变换是旋转的「升级版」。它可以做平移、旋转、缩放、剪切。说白了,就是保持「平行线还是平行线」的变换。

你需要3个点来定义变换矩阵。我一般选左上、右上、左下三个点。

# 原图中的三个点
pts1 = np.float32([[50,50], [200,50], [50,200]])
# 目标图中的三个点
pts2 = np.float32([[10,100], [200,50], [100,250]])
M = cv2.getAffineTransform(pts1, pts2)
transformed = cv2.warpAffine(img, M, (w, h))

我的习惯:做文档矫正时,我会手动标4个角点,然后用透视变换(getPerspectiveTransform)。仿射变换适合更「温和」的变形。

4.3 图像滤波

滤波,说白了就是「去噪」。你拍的照片有噪点,传感器不行、光线不好、压缩太狠,都会引入噪声。滤波就是把这些「脏东西」抹掉。

4.3.1 均值滤波

最简单。取一个窗口,算平均值。速度快,但会模糊边缘。

blurred = cv2.blur(img, (5, 5))  # 5x5的窗口

注意:窗口越大,越模糊。我一般用3x3或5x5。再大就糊成一片了。

4.3.2 高斯滤波

比均值滤波「聪明」一点。它给窗口中心的像素更高权重,边缘的权重低。所以模糊得更自然。

gaussian = cv2.GaussianBlur(img, (5, 5), 1.5)  # sigma=1.5

我的经验:做边缘检测前,我必先用高斯滤波。为什么?因为Canny边缘检测对噪声敏感。先滤波,再检测,效果天差地别。

4.3.3 中值滤波

这个我特别喜欢。它取窗口内所有像素的中位数。对付「椒盐噪声」(黑白点)特别有效。

median = cv2.medianBlur(img, 5)  # 5x5窗口

小技巧:中值滤波的窗口大小必须是奇数。3、5、7都行。我一般用5。太小效果不够,太大又模糊。

4.4 图像增强

图像增强,说白了就是「让图像更好看、更清晰」。不是给人类看,是给算法看。算法喜欢对比度高、亮度均匀的图像。

4.4.1 直方图均衡化

直方图均衡化,就是把图像的灰度分布「拉平」。原来暗的地方变亮,亮的地方变暗。让整个图像的对比度更均匀。

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
equalized = cv2.equalizeHist(gray)

避坑指南:直方图均衡化只对灰度图有效。彩色图要转成YUV或HSV,只对亮度通道做均衡化。我曾经直接对RGB三个通道分别做均衡化,结果颜色全乱了。

4.4.2 伽马校正

伽马校正,就是调整图像的「亮度曲线」。公式很简单:O = I ^ (1/gamma)

  • gamma < 1:图像变亮(暗部提升明显)
  • gamma > 1:图像变暗(亮部压缩)
  • gamma = 1:不变
gamma = 0.5  # 变亮
lookup_table = np.array([((i / 255.0) ** (1.0 / gamma)) * 255 for i in range(256)], dtype=np.uint8)
corrected = cv2.LUT(img, lookup_table)

我的习惯:做夜间图像处理时,我一般用 gamma=0.4~0.6。白天正常光照用 gamma=0.8~1.0。具体值要试,没有万能参数。

4.5 本章小结

好,这一章我们讲了图像预处理的三个核心模块:

  1. 几何变换:缩放、裁剪、旋转、仿射变换。让图像「摆正」。
  2. 图像滤波:均值、高斯、中值。让图像「干净」。
  3. 图像增强:直方图均衡化、伽马校正。让图像「清晰」。

这些操作,每一个我都用过上百次。说实话,预处理做得好,算法效果能提升10%以上。别小看这些「脏活累活」。

下一章,我们继续讲图像预处理的「下半场」——边缘检测、形态学操作、图像金字塔。到时候见。