文章目录
前言
这里开启我们的进阶教程——数据增强。这里我们单指二维图像增强,又可以称作图像处理操作。这一讲我会直接从平面图像底层讲到代码实现并给出python和c++的具体实现可以给自己更多自由发挥的空间。这一讲不要指望看一篇就能学会,我还是作抛砖引玉,更加具体的东西需要大家自己下去慢慢体会。
一、数据增强(图像增强)是什么?
简单来说,就是经过多种操作使得由一张原始图像生成略微区别于原始图像的操作。在计算机视觉任务中,数据增强(Data Augmentation)通过对原始图像进行随机变换生成多样化样本,能够有效缓解训练过拟合问题,同时提升模型泛化能力。
二、数据增强的核心目标与原则
1. 核心目标
扩大数据分布:模拟真实场景中的变化(如光照、视角、遮挡),增加训练数据多样性。比如智能车赛场上的上帝之光,这玩意也是可以模拟出来的
提升模型鲁棒性:让模型学习“不变性”(如旋转不变、颜色不变、尺度不变)。比如一个苹果,只要你保证它是完整的,那么不管它旋转多少度,整体外形是不变的,颜色是不变的。但是你如果给苹果颜色改成了紫色,那就没有必要了,因为这不符合实际。
防止过拟合:尤其在小数据集场景下,通过增强扩充样本量。不管怎么讲,正常情况下几千的规模和几百的规模相比,较多的会相对好一些
2. 设计原则
语义一致性:增强后图像的类别标签不变(如“猫”的图像旋转后仍是“猫”)。
可控性:增强强度可调节(如旋转角度范围、噪声方差),避免过度失真。就是说你自己写的代码你自己还控制不了么~
任务适配:分类任务侧重全局变换,检测/分割任务需同步变换边界框/掩码,这个就稍微复杂了一点,不过你可以单独对图像进行增强并不同步增强标签
三、核心增强手段分类与原理
1.基础几何变换(Geometric Transformations)
原理:通过空间变换模拟物体视角/位置变化,是最基础且普适的增强方法。
| 方法 | 原理 | 参数示例 | 注意事项 |
|---|---|---|---|
| 水平翻转 | 沿垂直轴镜像,模拟左右视角 | flip_code=1(OpenCV) | 适用于对称物体(动物、车辆),非对称物体(文字)禁用 |
| 垂直翻转 | 沿水平轴镜像,模拟上下视角 | flip_code=0 | 自然图像较少用,适用于天空/地面场景 |
| 旋转 | 绕中心点旋转θ角,模拟倾斜视角 | angle=15°, scale=1.0 | θ通常取[-30°, 30°],避免物体倒置 |
| 缩放 | 改变图像尺寸,模拟远近视角 | fx=0.8, fy=0.8(仿射) | 缩放后需resize回原尺寸,避免分辨率损失 |
| 裁剪 | 随机截取子区域,模拟局部视角 | crop_size=(224,224) | 与“中心裁剪”不同,随机裁剪更具多样性 |
| 平移 | 沿x/y轴偏移,模拟位置变化 | tx=10, ty=10(仿射) | 偏移量不超过图像尺寸的20%,避免关键区域丢失 |
2.颜色空间变换(Color Space Transformations)
原理:模拟不同光照、传感器或后期处理导致的颜色差异,增强模型对颜色变化的鲁棒性。
| 方法 | 原理 | 参数示例 | 效果 |
|---|---|---|---|
| 亮度调整 | 线性插值改变像素值范围 | alpha=0.8(变暗), 1.2(变亮) | 模拟光线强弱变化 |
| 对比度调整 | 拉伸像素值分布区间 | alpha=1.5(高对比度), 0.7(低对比度) | 模拟曝光不足/过曝 |
| 饱和度调整 | 在HSV空间调整S通道值 | saturation=0.6(低饱和), 1.4(高饱和) | 模拟不同色温光源 |
| 色调调整 | 在HSV空间调整H通道值 | hue=0.1(偏红), -0.1(偏蓝) | 模拟色偏(如老照片、滤镜) |
| Gamma校正 | 非线性调整亮度(I_out = I_in^γ) | gamma=1.5(提亮暗部), 0.7(压暗高光) | 模拟非线性传感器响应 |
| 白平衡 | 调整RGB通道增益,校正色偏 | gain_r=1.1, gain_g=0.9, gain_b=1.0 | 模拟不同光源(日光、白炽灯) |
3.噪声与模糊(Noise & Blur)
原理:模拟传感器噪声、运动模糊或失焦模糊,提升模型对低质量图像的鲁棒性。
| 方法 | 原理 | 参数示例 | 效果 |
|---|---|---|---|
| 高斯噪声 | 叠加均值为0、方差σ²的高斯分布噪声 | mean=0, sigma=25 | 模拟传感器热噪声 |
| 椒盐噪声 | 随机替换像素为黑/白点 | amount=0.05(噪声占比5%) | 模拟信号干扰 |
| 高斯模糊 | 卷积高斯核平滑图像 | kernel_size=(5,5), sigma=1.5 | 模拟失焦模糊 |
| 运动模糊 | 卷积方向核模拟运动轨迹 | kernel_size=15, angle=45° | 模拟相机/物体运动 |
4.高级语义保持增强(Semantic-Preserving Augmentations)
原理:在基础变换基础上,通过遮挡、混合等方式强制模型学习更本质的特征,适用于复杂场景。
| 方法 | 原理 | 参数示例 | 优势 |
|---|---|---|---|
| Cutout | 随机遮挡矩形区域(填充黑/白) | mask_size=(32,32), ratio=0.5 | 强制模型关注局部特征,防遮挡鲁棒性 |
| Mixup | 线性插值图像与标签:λ~Beta(α,α) | alpha=0.4 | 学习图像间过渡状态,提升分类边界鲁棒性 |
| CutMix | 用另一图像的矩形区域替换当前图像,标签按面积加权 | beta=1.0 | 比Mixup更关注局部结构,避免模糊标签 |
| AutoAugment | 强化学习搜索最优增强策略组合 | 预定义策略(如ImageNet策略) | 自动适配任务,无需手动调参 |
| RandAugment | 随机选择N种基础增强,统一强度 | n_ops=2, magnitude=5 | 轻量高效,替代AutoAugment |
5.数据与标签同步增强手段
目标检测:需同步变换边界框(如旋转后更新框坐标,缩放后调整框尺寸),工具:albumentations的BBoxParams。
语义分割:需同步变换掩码(Mask),确保像素级对齐,工具:albumentations的MaskParams。
四、Python实战代码(OpenCV/albumentations/torchvision)
1. 基础几何+颜色变换(OpenCV)
import cv2
import numpy as np
import matplotlib.pyplot as plt
def apply_geometric_color_aug(img_path):
# 读取图像
img = cv2.imread(img_path)[..., ::-1] # BGR转RGB
# 1. 几何变换:随机旋转(-15°~15°)+ 缩放(0.8~1.2倍)
h, w = img.shape[:2]
center = (w//2, h//2)
angle = np.random.uniform(-15, 15)
scale = np.random.uniform(0.8, 1.2)
M_rotate = cv2.getRotationMatrix2D(center, angle, scale)
img_rot = cv2.warpAffine(img, M_rotate, (w, h), borderMode=cv2.BORDER_REFLECT)
# 2. 颜色变换:亮度(0.7~1.3)+ 对比度(0.8~1.2)+ 饱和度(0.7~1.3)
img_hsv = cv2.cvtColor(img_rot, cv2.COLOR_RGB2HSV)
h, s, v = cv2.split(img_hsv)
s = np.clip(s * np.random.uniform(0.7, 1.3), 0, 255).astype(np.uint8)
v = np.clip(v * np.random.uniform(0.7, 1.3), 0, 255).astype(np.uint8)
img_color = cv2.cvtColor(cv2.merge([h, s, v]), cv2.COLOR_HSV2RGB)
# 3. 噪声:高斯噪声(σ=15)
noise = np.random.normal(0, 15, img_color.shape).astype(np.uint8)
img_final = cv2.add(img_color, noise)
plt.figure(figsize=(12, 4))
plt.subplot(131), plt.imshow(img), plt.title("Original")
plt.subplot(132), plt.imshow(img_rot), plt.title("Rotated+Scaled")
plt.subplot(133), plt.imshow(img_final), plt.title("Augmented")
plt.show()
# 使用示例
apply_geometric_color_aug("1.jpg")

2. 高级增强(albumentations,支持分类/检测/分割)
import albumentations as A
from albumentations.pytorch import ToTensorV2
import cv2
# 定义增强管道(分类任务)
transform_class = A.Compose([
A.HorizontalFlip(p=0.5), # 50%概率水平翻转
A.Rotate(limit=15, p=0.3), # 30%概率旋转(-15°~15°)
A.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2, hue=0.1, p=0.5),
A.GaussNoise(var_limit=(10, 50), p=0.3), # 高斯噪声(方差10~50)
A.Cutout(num_holes=1, max_h_size=32, max_w_size=32, p=0.5), # Cutout
ToTensorV2() # 转PyTorch Tensor
])
# 定义检测任务增强(含边界框)
transform_det = A.Compose([
A.RandomSizedCrop(min_max_height=(200, 300), height=300, width=300, p=0.5),
A.ShiftScaleRotate(shift_limit=0.1, scale_limit=0.2, rotate_limit=15, p=0.5),
A.RGBShift(r_shift_limit=15, g_shift_limit=15, b_shift_limit=15, p=0.3),
A.MotionBlur(blur_limit=15, p=0.3),
A.BBoxParams(format="pascal_voc") # 同步变换边界框(PASCAL VOC格式)
], bbox_params=A.BBoxParams(format="pascal_voc"))
# 使用示例(分类)
img = cv2.imread("dog.jpg")[..., ::-1] # BGR转RGB
augmented = transform_class(image=img)
plt.imshow(augmented["image"].permute(1,2,0)) # Tensor转RGB显示
# 使用示例(检测,假设bboxes为[[x_min,y_min,x_max,y_max,class_id],...])
bboxes = [[50, 60, 200, 300, 1]] # 示例边界框
augmented_det = transform_det(image=img, bboxes=bboxes)
augmented_img = augmented_det["image"]
augmented_bboxes = augmented_det["bboxes"] # 同步变换后的边界框
3. PyTorch内置增强(torchvision.transforms)
import torchvision.transforms as transforms
from PIL import Image
# 分类任务增强管道
transform_torch = transforms.Compose([
transforms.RandomHorizontalFlip(), # 随机水平翻转
transforms.RandomRotation(15), # 随机旋转(-15°~15°)
transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2), # 颜色抖动
transforms.RandomGrayscale(p=0.1), # 10%概率转灰度图
transforms.ToTensor(), # PIL转Tensor
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 归一化
])
# 使用示例
img_pil = Image.open("bird.jpg")
augmented_img = transform_torch(img_pil)
print(augmented_img.shape) # torch.Size([3, H, W])
五、C++实战代码(OpenCV实现核心增强)
C++主要依赖OpenCV,适合高性能部署或嵌入式场景,以下为几何+颜色+噪声增强示例:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <random>
using namespace cv;
using namespace std;
// 随机数生成器(C++11)
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
// 1. 随机旋转(-15°~15°)
Mat random_rotate(const Mat& img) {
Point2f center(img.cols / 2.0f, img.rows / 2.0f);
uniform_real_distribution<float> angle_dist(-15, 15);
float angle = angle_dist(rng);
Mat M = getRotationMatrix2D(center, angle, 1.0);
Mat dst;
warpAffine(img, dst, M, img.size(), INTER_LINEAR, BORDER_REFLECT);
return dst;
}
// 2. 颜色抖动(亮度+对比度)
Mat color_jitter(const Mat& img) {
uniform_real_distribution<float> brightness_dist(0.7, 1.3);
uniform_real_distribution<float> contrast_dist(0.8, 1.2);
float brightness = brightness_dist(rng);
float contrast = contrast_dist(rng);
Mat hsv;
cvtColor(img, hsv, COLOR_BGR2HSV);
vector<Mat> channels;
split(hsv, channels);
// 调整V通道(亮度)和S通道(饱和度)
channels[2] = channels[2] * brightness;
channels[1] = channels[1] * contrast;
merge(channels, hsv);
Mat dst;
cvtColor(hsv, dst, COLOR_HSV2BGR);
return dst;
}
// 3. 高斯噪声
Mat add_gaussian_noise(const Mat& img) {
Mat noise(img.size(), img.type());
randn(noise, 0, 15); // 均值0,方差15
Mat dst;
add(img, noise, dst);
return dst;
}
int main() {
// 读取图像
Mat img = imread("car.jpg");
if (img.empty()) {
cerr << "Error loading image!" << endl;
return -1;
}
// 应用增强
Mat rotated = random_rotate(img);
Mat jittered = color_jitter(rotated);
Mat noisy = add_gaussian_noise(jittered);
// 显示结果
imshow("Original", img);
imshow("Augmented", noisy);
waitKey(0);
return 0;
}
总结
对于原始数据规模较小的情况,我们应该优先用强增强(Cutout、Mixup、RandAugment),增加样本的多样性;如果数据本身规模就很大,那我们用基础增强即可,避免过度扰动对模型造成不必要干扰。
注意针对于非对称物体(如文字、车牌)禁用水平翻转;医学影像慎用颜色抖动;噪声方差、旋转角度需根据数据分布调整。
经过本章内容,你应该写出了独具你个人风格的增强代码,我曾经要求我带的同学都要自己写自己需要的增强代码作为比赛的需要。在实际比赛过程中我更倾向于使用Python用albumentations(功能全)进行快速的图像增强,而在上板计算机视觉这个东西到时候使用的时候往往更是一种经验,对于一种图像任务能够很快反应出来其适合的增强手段说明你学到家了。那么你学会了么?

806

被折叠的 条评论
为什么被折叠?



