📢 公告:OpenCV 系列文章目前正在重構整理中(進度約 60%),部分文章已暫時下架,後續會陸續補上,完成時間待定。感謝耐心等候!

Like Share Discussion Bookmark Smile

J.J. Huang   2026-03-30   Python OpenCV 07.物件偵測與辨識篇   瀏覽次數:次   DMCA.com Protection Status

Python | OpenCV 資料增強

📚 前言

在上一篇 TensorFlow/Keras 微調範例 中,我們完成了兩階段微調的完整流程。
這一篇深入介紹 資料增強 (Data Augmentation) 的原理與各種常用技巧。

前兩篇的訓練腳本中已經各自用了少量資料增強:

🔎 資料增強的原理

增強不會在磁碟上產生新圖片

很多人以為資料增強是把 000001.jpg(貓)翻轉後另存成 000002.jpg,讓磁碟上的圖片數量增加。
實際上完全不是這樣。增強發生在記憶體中、訓練的當下,原始圖片永遠不會被修改或複製。

1
2
3
4
5
6
7
8
9
❌ 常見誤解:增強 = 另存新圖(磁碟上的圖片數量增加)
000001.jpg → [翻轉] → 000002.jpg
000001.jpg → [旋轉] → 000003.jpg

✅ 實際做法:增強在每次讀取時於記憶體中即時發生(磁碟完全不變)
Epoch 1:讀取 000001.jpg → [即時左右翻轉] → 餵給模型
Epoch 2:讀取 000001.jpg → [即時旋轉 8°] → 餵給模型
Epoch 3:讀取 000001.jpg → [亮度 +12%,不翻轉] → 餵給模型
(000001.jpg 始終是那一張,磁碟上沒有新增任何圖片)

DataLoader 每次從磁碟讀取圖片,就在記憶體中套用一組隨機的變換,然後把這個即時生成的版本餵給模型。
原始檔案不受影響,下次讀取同一張圖時又是另一組隨機變換。

訓練 15 個 epoch,模型實際上看過 15 個版本的 000001.jpg,但磁碟上始終只有那一張。

增強的核心假設

資料增強的假設是:圖片在合理的變換後,其類別標籤不應改變

例如,一張貓的圖片左右翻轉後仍然是貓;亮度調暗後仍然是貓。
透過這些變換,模型學會了不依賴特定的方向、亮度或細節來辨識類別,從而提升泛化能力。

💡 資料增強只在訓練集使用,驗證集與測試集不做增強,確保評估結果真實反映模型的泛化能力。

🧠 常用增強技巧


圖:常用資料增強技巧 ─ 各種增強方式的簡單說明與適合使用情境

🛠️ 環境安裝

本篇以三種工具示範資料增強的實作,各有不同的整合方式與適用場景:


圖:常用資料增強套件比較 ─ torchvision.transforms、tf.keras.layers 與 albumentations 的特色差異

1
2
3
4
# 只裝你需要的就好,不用三個都裝
pip install torch torchvision # PyTorch 用
pip install tensorflow # TensorFlow/Keras 用
pip install albumentations # 最強的增強工具(推薦)

💡 先看效果,再看設定

在進入三種工具的完整設定之前,先用十幾行程式看看增強的效果。
這段程式碼只需要 torchvision,不需要 DataLoader 或模型,執行後你會看到來自同一張圖片的五種不同隨機結果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# simple_augment_demo.py
from PIL import Image
import matplotlib.pyplot as plt
from torchvision import transforms

img = Image.open("assets/test.png").convert("RGB")

augment = transforms.Compose([
transforms.RandomHorizontalFlip(p=1.0), # p=1.0 = 每次都翻轉(方便觀察效果)
transforms.RandomRotation(20), # 隨機旋轉最多 ±20 度
transforms.ColorJitter(brightness=0.4), # 隨機調整亮度 ±40%
])

fig, axes = plt.subplots(1, 5, figsize=(15, 3))
axes[0].imshow(img)
axes[0].set_title("原始")
for i in range(1, 5):
axes[i].imshow(augment(img)) # 同一張圖,每次呼叫都得到不同結果
axes[i].set_title(f"增強 {i}")
plt.tight_layout()
plt.savefig("output/simple_augment_demo.png")
plt.show()


圖:對同一張圖片連續套用增強管線四次並並排顯示,每次的翻轉角度與亮度都不同

看完這個效果之後,下面的三種工具就是教你如何把相同的邏輯整合進 DataLoader 和訓練迴圈。

💻 PyTorch — torchvision.transforms

PyTorch 微調範例train.py 裡,train_transform 只有三個基本增強。
以下示範的完整管線可以直接替換 train.pytrain_transform 定義,訓練迴圈的其他部分不需要修改。


圖:PyTorch 資料載入與即時增強流程 ─ 從資料夾到模型輸入 Tensor 的完整過程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# augment_pytorch.py
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# 訓練集:完整增強管線(可直接替換 train.py 的 train_transform)
train_transform = transforms.Compose([
transforms.Resize((256, 256)), # 先放大,為 RandomCrop 留出裁切空間
transforms.RandomCrop(224), # 隨機裁切到目標尺寸 224×224
transforms.RandomHorizontalFlip(p=0.5), # 50% 機率左右翻轉
transforms.RandomVerticalFlip(p=0.2), # 20% 機率上下翻轉
transforms.RandomRotation(degrees=15), # 隨機旋轉 ±15 度
transforms.ColorJitter(
brightness=0.3, # 亮度隨機調整 ±30%
contrast=0.3, # 對比隨機調整 ±30%
saturation=0.3, # 飽和度隨機調整 ±30%
hue=0.1 # 色調隨機調整 ±10%
),
transforms.RandomAffine(
degrees=0,
translate=(0.1, 0.1), # 隨機平移最多 10%
scale=(0.9, 1.1) # 隨機縮放 90%~110%
),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406],
[0.229, 0.224, 0.225]) # ImageNet 標準均值與標準差
])

# 驗證集:只做必要的 Resize 與 Normalize,絕對不增強
val_transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])
])

# 套用到 Dataset 與 DataLoader(與 train.py 結構完全相同)
train_dataset = datasets.ImageFolder("data/cat_dog/train", transform=train_transform)
val_dataset = datasets.ImageFolder("data/cat_dog/val", transform=val_transform)

# num_workers=0:Windows 上若使用多進程可能發生死鎖,建議設 0
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=0)
val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False, num_workers=0)

# 取出一個 batch 確認形狀(此時才真正觸發一次讀取與增強)
images, labels = next(iter(train_loader))
print(f"Batch shape : {images.shape}") # torch.Size([32, 3, 224, 224])
print(f"Label shape : {labels.shape}") # torch.Size([32])
print(f"Classes : {train_dataset.classes}")


圖:定義 torchvision.transforms 增強管線並整合至 ImageFolder 與 DataLoader,印出 batch 形狀確認資料流正確

視覺化增強結果

把完整的增強管線套用在單張圖片上,看看每次的隨機結果有多大差異。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# visualize_augment_pytorch.py
import matplotlib.pyplot as plt
from PIL import Image
from torchvision import transforms

img = Image.open("assets/test.png").convert("RGB")

augment = transforms.Compose([
transforms.Resize((224, 224)),
transforms.RandomHorizontalFlip(p=1.0),
transforms.ColorJitter(brightness=0.4, contrast=0.4),
transforms.RandomRotation(20),
])

fig, axes = plt.subplots(1, 5, figsize=(15, 3))
axes[0].imshow(img)
axes[0].set_title("原始")
for i in range(1, 5):
axes[i].imshow(augment(img))
axes[i].set_title(f"增強 {i}")
plt.tight_layout()
plt.savefig("output/augmentation_preview.png")
plt.show()




圖:對同一張圖片套用 PyTorch 完整增強管線四次並並排顯示,直觀比較各次增強的隨機效果

💻 TensorFlow/Keras — 增強層

TensorFlow/Keras 微調範例train.py 裡,data_augmentation 只有四層基本增強。
以下展示更完整的增強層組合,可以直接替換 train.pydata_augmentation 定義,不需要修改其他程式碼。


圖:TensorFlow 資料載入與自動增強流程 ─ image_dataset_from_directory 與資料增強層的完整過程

Keras 的增強層嵌入模型架構內部,model.fit 時自動啟用、model.predict 時自動關閉,不需要手動切換。
這是 Keras 與 PyTorch 在增強實作上最大的差異:PyTorch 在 DataLoader 層處理,Keras 在模型內部處理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# augment_keras.py
import tensorflow as tf
from tensorflow.keras import layers

# 增強層定義(可直接替換 train.py 的 data_augmentation 定義)
data_augmentation = tf.keras.Sequential([
layers.RandomFlip("horizontal"), # 隨機左右翻轉
layers.RandomFlip("vertical"), # 隨機上下翻轉
layers.RandomRotation(0.15), # 隨機旋轉 ±15%(約 ±54 度)
layers.RandomZoom(0.15), # 隨機縮放 ±15%
layers.RandomTranslation(0.1, 0.1), # 隨機平移 ±10%
layers.RandomBrightness(0.2), # 隨機調整亮度 ±20%
layers.RandomContrast(0.2), # 隨機調整對比 ±20%
], name="data_augmentation")

# 建立含增強層的完整模型(結構與 train.py 相同)
base_model = tf.keras.applications.MobileNetV2(
input_shape=(224, 224, 3), include_top=False, weights="imagenet"
)
base_model.trainable = False

inputs = tf.keras.Input(shape=(224, 224, 3))
x = data_augmentation(inputs) # ← 增強發生在這裡
x = tf.keras.applications.mobilenet_v2.preprocess_input(x)
x = base_model(x, training=False)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(128, activation="relu")(x)
outputs = layers.Dense(2, activation="softmax")(x) # 2 個類別:cat / dog
model = tf.keras.Model(inputs, outputs)

model.compile(optimizer="adam",
loss="sparse_categorical_crossentropy",
metrics=["accuracy"])

train_ds = tf.keras.utils.image_dataset_from_directory(
"data/cat_dog/train", image_size=(224, 224), batch_size=32
)
val_ds = tf.keras.utils.image_dataset_from_directory(
"data/cat_dog/val", image_size=(224, 224), batch_size=32
)

# model.fit 訓練時 data_augmentation 自動啟用;驗證 val_ds 時自動關閉
model.fit(train_ds, validation_data=val_ds, epochs=10)


圖:將 Keras 增強層嵌入 MobileNetV2 模型架構,以 image_dataset_from_directory 載入資料後執行 model.fit 訓練

視覺化增強結果

在模型架構之外,手動呼叫增強層對單張圖片套用四次,確認增強效果是否符合預期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# visualize_augment_keras.py
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
import cv2

img = cv2.imread("assets/test.png")
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_tensor = tf.expand_dims(img_rgb, 0) # 加上 batch 維度(增強層需要 4D Tensor)

augment = tf.keras.Sequential([
tf.keras.layers.RandomFlip("horizontal"),
tf.keras.layers.RandomRotation(0.15),
tf.keras.layers.RandomBrightness(0.3),
])

fig, axes = plt.subplots(1, 5, figsize=(15, 3))
axes[0].imshow(img_rgb)
axes[0].set_title("原始")
for i in range(1, 5):
# training=True:告訴增強層現在要套用隨機變換,否則它以為是推論階段而不做增強
aug_img = augment(img_tensor, training=True)[0].numpy().astype(np.uint8)
axes[i].imshow(aug_img)
axes[i].set_title(f"增強 {i}")
plt.tight_layout()
plt.savefig("output/augmentation_preview_keras.png")
plt.show()




圖:使用 TensorFlow Keras 增強層對圖片套用四次隨機增強並並排視覺化比較

💻 Albumentations — 更豐富的增強選項

Albumentations 是 torchvision.transforms 的替代方案,整合方式對應 PyTorch 微調範例 的 DataLoader 流程,但提供更豐富的增強種類,也支援物件偵測任務的邊界框同步變換(翻轉、旋轉時邊界框座標自動跟著調整)。


圖:使用 Albumentations 的 PyTorch 資料載入流程 ─ 自訂 Dataset + Albumentations 增強管線

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# augment_albumentations.py
import albumentations as A
from albumentations.pytorch import ToTensorV2
import cv2
import os
from torch.utils.data import Dataset, DataLoader

# 訓練集增強管線(可替換 train.py 中的 train_transform)
train_transform = A.Compose([
A.Resize(224, 224),
A.HorizontalFlip(p=0.5), # 50% 機率左右翻轉
A.VerticalFlip(p=0.2), # 20% 機率上下翻轉
A.Rotate(limit=15, p=0.5), # 隨機旋轉 ±15 度
A.RandomBrightnessContrast(p=0.4), # 隨機調整亮度與對比
A.GaussNoise(var_limit=(10.0, 50.0), p=0.2), # 加入高斯雜訊
A.Blur(blur_limit=3, p=0.2), # 輕微模糊
A.CLAHE(p=0.2), # 自適應直方圖均衡化
A.CoarseDropout(p=0.2), # 隨機遮蔽小區塊(模擬資訊缺失)
A.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]),
ToTensorV2()
])

# 驗證集:只做 Resize 與標準化
val_transform = A.Compose([
A.Resize(224, 224),
A.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]),
ToTensorV2()
])

# Albumentations 無法直接傳給 ImageFolder,需要自訂 Dataset
class ImageDataset(Dataset):
def __init__(self, root, transform=None):
self.transform = transform
self.classes = sorted(os.listdir(root))
self.samples = []
for idx, cls in enumerate(self.classes):
cls_dir = os.path.join(root, cls)
for fname in os.listdir(cls_dir):
self.samples.append((os.path.join(cls_dir, fname), idx))

def __len__(self):
return len(self.samples)

def __getitem__(self, i):
path, label = self.samples[i]
img = cv2.imread(path)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
if self.transform:
img = self.transform(image=img)["image"] # Albumentations 用字典格式傳入與取出
return img, label

train_dataset = ImageDataset("data/cat_dog/train", transform=train_transform)
val_dataset = ImageDataset("data/cat_dog/val", transform=val_transform)

# num_workers=0:Windows 上設 0 避免死鎖
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=0)
val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False, num_workers=0)

images, labels = next(iter(train_loader))
print(f"Batch shape : {images.shape}") # torch.Size([32, 3, 224, 224])
print(f"Classes : {train_dataset.classes}")


圖:以自訂 Dataset 整合 Albumentations 增強管線,透過 DataLoader 取出一個 batch 並印出形狀確認

視覺化增強結果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# visualize_augment_albumentations.py
import albumentations as A
import cv2
import matplotlib.pyplot as plt

augment = A.Compose([
A.HorizontalFlip(p=1.0),
A.RandomBrightnessContrast(p=1.0),
A.Rotate(limit=20, p=1.0),
A.GaussNoise(var_limit=(10.0, 50.0), p=0.5),
])

img = cv2.imread("assets/test.png")
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

fig, axes = plt.subplots(1, 5, figsize=(15, 3))
axes[0].imshow(img)
axes[0].set_title("原始")
for i in range(1, 5):
aug_img = augment(image=img)["image"] # Albumentations 用字典格式傳入與取出
axes[i].imshow(aug_img)
axes[i].set_title(f"增強 {i}")
plt.tight_layout()
plt.savefig("output/albumentations_preview.png")
plt.show()




圖:對同一張圖片套用 Albumentations 增強管線四次並並排顯示,比較翻轉、旋轉、雜訊的隨機效果

⚠️ 注意事項

  • 不是所有增強都適用所有任務:垂直翻轉對自然場景的分類任務通常沒有幫助(天空在上、地面在下);文字辨識任務也不能做大幅旋轉。選擇增強前,先想清楚「這個變換後的圖片,人還是能正確辨識嗎?」
  • 增強強度要適中:過度增強(如旋轉 90 度、極端色彩變換)可能讓圖片失去原有的語意,反而降低訓練效果。建議從常用組合開始,若準確率未改善再調整強度。
  • 驗證集不做增強:驗證集只做 Resize 與 Normalize,不加任何增強,確保評估結果穩定、可比較。
  • 物件偵測要同步變換標註:對偵測任務做幾何增強(翻轉、旋轉)時,邊界框座標也必須一起變換。使用 Albumentations 時,在 A.Compose 加入 bbox_params=A.BboxParams(format="yolo") 可自動處理。
  • Windows 上 num_workers 設 0:PyTorch DataLoader 在 Windows 上使用多進程可能造成死鎖,建議設為 0;Linux / macOS 可設 2 或 4 提升效率。

📊 應用場景

  • 分類任務資料不足:以 PyTorch transforms 或 Keras 增強層快速擴充訓練集,不需額外收集資料。
  • 物件偵測與標註同步:Albumentations 支援邊界框、遮罩的同步變換,是 YOLO、Faster R-CNN 等偵測任務的主流增強工具。
  • 醫療影像:病理切片、X 光片可用隨機旋轉與翻轉增強,但不適合大幅色彩變換(色彩可能帶有診斷意義)。
  • 衛星與航拍影像:沒有固定方向,適合同時使用水平與垂直翻轉,大幅擴充訓練資料。
  • 工廠品管:瑕疵偵測場景可加入高斯雜訊與模糊,模擬不同品質的相機鏡頭效果。

🎯 結語

資料增強是提升模型泛化能力最直接的方式,尤其在資料量有限的情況下效果顯著。
PyTorch 的 transforms 整合 DataLoader、Keras 的增強層嵌入模型架構、Albumentations 支援最豐富的增強種類,三者各有適用場景,可依框架與任務需求選擇。
下一步是 避免過擬合,進一步強化模型的泛化能力。

📖 如在學習過程中遇到疑問,或是想了解更多相關主題,建議回顧一下 Python | OpenCV 系列導讀,掌握完整的章節目錄,方便快速找到你需要的內容。

註:以上參考了
PyTorch 官方文件 — torchvision.transforms
TensorFlow 官方文件 — Preprocessing Layers
Albumentations 官方文件