🦉

セグメンテーションにおけるデータ拡張と Albumentations の活用

に公開

セグメンテーション特有のデータ拡張

セグメンテーションタスクでは、画像と対応するマスク(GTマスク)の両方に対して、同じ幾何的変換を適用する必要があります。例えば画像を回転した場合、対応するマスクもまったく同じように回転しないといけないですよね。
一方で、画像にはコントラスト調整や色味の補正といった数値的な変換を施したいけど、マスクには何もしたくないという需要もあります。

こうした変換処理を毎回手作業で実装するのはかなり手間がかかりますが、Albumentations を使えば、一つの Compose に画像とマスクを同時に渡すだけで、必要な変換を自動的に適用してくれます。


幾何変換と数値変換の自動判別

Albumentations は transform の内容に応じて、以下のように自動的に処理を分けてくれます。

  • 幾何的な変換(例:回転・スケーリング・フリップ) → imagemask の両方に同じ処理が適用される
  • 数値的な変換(例:明るさ・コントラスト・ノイズなど) → image のみに適用され、mask には影響しない

この動作によって、セグメンテーションの画像・マスクペアの整合性を保ったまま拡張が可能になります。


実装例:幾何+数値変換

from PIL import Image
import numpy as np
import albumentations as A
from albumentations.pytorch import ToTensorV2

# Albumentations の transform(幾何変換 + 数値変換)
transform = A.Compose([
    A.HorizontalFlip(p=0.5),                      # 幾何:左右反転(image & mask)
    A.RandomCrop(width=256, height=256),          # 幾何:ランダムクロップ(image & mask)
    A.Rotate(limit=45, p=0.5),                    # 幾何:ランダム回転(image & mask)
    A.RandomBrightnessContrast(p=0.2),            # 数値:明るさ・コントラスト調整(image のみ)
    A.GaussianBlur(blur_limit=3, p=0.5),          # 数値:ガウシアンブラー(image のみ)
    ToTensorV2()
])

# PIL で画像とマスクを読み込む
image_pil = Image.open("img.png").convert("RGB")
mask_pil = Image.open("label.png").convert("L")

# PIL → NumPy に変換(Albumentations は np.ndarray を要求)
image_np = np.array(image_pil)
mask_np = np.array(mask_pil)

# Albumentations による変換
transformed = transform(image=image_np, mask=mask_np)

# 変換後の画像とマスクを取得(torch.Tensor 型)
transformed_image = transformed["image"]
transformed_mask = transformed["mask"]

変換前の画像と後の画像

画像 マスク
変換後画像 変換後マスク

補足:mask は整数値ラベルが前提

Albumentations の mask 処理は、離散ラベル(例:クラスID)を壊さずに変換してくれます。マスクに対して色補正やノイズをかけないことで、ラベル情報が失われる心配もありません。


DualTransform を用いたカスタムのデータ拡張

Albumentations の強みのひとつに、独自のデータ拡張処理を簡単に追加できる拡張性があります。
DualTransform を継承すると、画像 (image) とマスク (mask) の両方に幾何変換を一貫して適用できるカスタム変換が作れます。

以下は、画像とマスクを一定確率で垂直方向に反転させるカスタム変換の例です。

class CustomVerticalFlip(A.DualTransform):
    def __init__(self, always_apply=False, p=0.5):
        super().__init__(always_apply, p)

    def apply(self, img, **params):
        return np.flipud(img)  # 画像を上下反転

    def apply_to_mask(self, mask, **params):
        return np.flipud(mask)  # マスクも上下反転

    def get_transform_init_args_names(self):
        return ()

上記の、apply() と apply_to_mask() に画像とマスクそれぞれへの変換を記述することが可能です


※注意

Albumentations は基本的に NumPy 配列(np.ndarray)を入力に取るため、torchvision.transforms のノリで PIL.Image をそのまま渡すとエラーになります。

そのため、以下のように、PIL 画像を np.array() で変換してから transform() を適用する必要があります。

# PIL 画像の読み込み
image_pil = Image.open("example.jpg").convert("RGB")

# PIL → NumPy に変換
image_np = np.array(image_pil)

# Albumentations の transform 定義
transform = A.Compose([
    A.Resize(256, 256),
    A.HorizontalFlip(p=0.5),
    A.Normalize(mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5)),
    ToTensorV2()
])

# Albumentations に適用
transformed = transform(image=image_np)
image_tensor = transformed["image"]  # PyTorch Tensor として取得

各変換を試したい場合、Alubmentations TransformsでWeb上でパラメータをいじって試せるので便利です


さいごに

Albumentations は、セグメンテーションにおけるデータ拡張を 柔軟かつ安全に自動化する非常に優れたライブラリです。実務でも広く使われており、デフォルトの変換に加えて カスタム変換も簡単に追加できるため、表現力も高いです。今後のタスクにぜひ活用してみてください。

Fusic 技術ブログ

Discussion