機械学習
記事内に商品プロモーションを含む場合があります

物体検出でも使えるAlbumentationの使い方(画像データ拡張, Data Augmentation)

tadanori

コンピュータービジョン用のデータ拡張(Data Augmentation)ライブラリとして有名なalbumentationsの使い方を解説します。このライブラリ、物体検出の枠(BBOX)やセグメンテーションのマスクデータがある場合も、枠やマスク情報を含めて変換してくれるかなり便利なライブラリです

データ拡張(Data Augmentation)とは

データ拡張(Data Augmentation)は、訓練データセット内のサンプル数を増やすために、元のデータを変換・変更する手法です。

データ拡張の主要な目的は、モデルの汎化能力を向上させ、過学習を防ぎ新しいデータに対する性能を向上させることです。これにより、訓練データが限られている場合でも、より強力なモデルを構築できます。

データ拡張をは、ディープラーニングでは非常に有用な手法の1つで、学習時だけでなく、推論時にも利用したりします(TTA、Test Time Augumentation)。

推論時の使い方としては、例えば、データ拡張しない画像と、左右フリップした画像に対してそれぞれ推論を行い、結果をアンサンブルするなどの使い方があります。これにより精度の向上が見込めます

Albumentationsとは

Albumentationsは、コンピュータビジョンタスク用のデータ拡張のためのライブラリです。画像認識(クラス分類)だけでなく、物体検出タスクセグメンテーションタスクにも対応していいることが特徴です。

この記事では、Albumentationsの使い方について説明します。

torchvisionのtransformsもv2で「物体検出タスク」、「セグメンテーションタスク」に対応しています。詳しくは、こちらの記事を参照してください。

物体検出をサポートしたデータ拡張の使い方【torchvision.transofrms.v2】
物体検出をサポートしたデータ拡張の使い方【torchvision.transofrms.v2】

インストール

Albumentationのインストールも簡単です。Google Colabでは以下のコードを実行します。ローカルな環境では、先頭の!を外してコマンドラインで実行します。

!pip install -U albumentations

基本的な使い方

以下基本的な使い方です。

まず、ライブラリをインポートします。画像の読み込みにOpenCVを利用したいと思いますので、cv2も一緒にインポートしておきます。

import albumentations as A
import cv2

Albumentationでは、複数のデータ拡張を組みあせて使うことが多いです。組み合わせる場合は、以下のようにComposeを利用します。

以下の例では、RandomCropHorizontalFlipRandomBrightnessContrastの3つの変換を順番に処理します。Composeは、リストに列挙された処理を順番に処理する関数です。

ほとんどの関数に引数pが指定できます。これは、処理する確率(確率)を指定するものです。p=1にした場合は必ず処理され、p=0.5にした場合は50%の確率で処理されます。

transform = A.Compose([
    A.RandomCrop(width=256, height=256),
    A.HorizontalFlip(p=0.5),
    A.RandomBrightnessContrast(p=0.2),
])

定義した変換は、以下のようにして利用します。img = cv2.cv2Color(img, cv2.COLOR_BGR2RGB)は、読み込んだ画像のフォーマットがBGRでRGBに変換の必要がある場合に挿入します。

img = cv2.imread('/path/to/image.jpg')
img = cv2.cv2Color(img, cv2.COLOR_BGR2RGB)
transformed_img = transform(image = img)

以下、代表的な合成関連のAPIです。他にもいくつかAPIが用意されていますが、Composeと、OneOfだけで大体の場合は記述できます。引数などの詳しい情報は、公式ページを参考にしてください。

重複(他で代用できそうなもの)は省略しています。

Compose

リストに列挙された処理を順番に処理します。

OneOf

リストに列挙された処理の1つを選択して処理します。複数の処理のうち、1つを選んで処理したい場合に利用します

PerChannel

チャネルごとに変換を行います。

以下は、ComposeOneOfを組み合わせた例です。この例では、RandomCropは必ず実行され、HorizontalFlipRandomBrightnessContrastはどちらか一方が50%の確率で適用されます。

transform = A.Compose([
    A.RandomCrop(width=256, height=256),
    A.OneOf([
      A.HorizontalFlip(),
      A.RandomBrightnessContrast(),
    ], p = 0.5)
])

Transforms

Transformsとして分類されている機能の一部です

ChannelShuffle

RGBをランダムにシャッフルします

result = A.ChannelShuffle(p=1)(image=img)
Original
Original
ChannelShuffle
ChannelShuffle()

CLAHE

Contrast Limited Adaptive Histogram Equalization(CLAHE, 適応的ヒストグラム平滑化)を適用します。コントラストが低い映像がはっきりします。

result = A.CLAHE(p=1)(image=img)
Original
Original
CLAHE
CLAHE()

ColorJitter

カラージッターを挿入します。brightness, contrast, saturation, hueそれぞれのジッター量を指定できます。

result = A.ColorJitter(brightness=1.0, hue=(-0.5,0.5), p=1)(image=img)
Original
Original
ColorJitter
ColorJitter()
ColorJitter(brightness=1.0, hue=(-0.5,0.5))
ColorJitter(brightness=1.0, hue=(-0.5,0.5))
主要パラメータ
brightnessfloatまたは(min, max)で指定
contrastfloatまたは(min, max)で指定
saturationfloatまたは(min, max)で指
huefloatまたは(min, max)で指定。-0.5~0.5の範囲で指定

Downscale

縮小と拡大を行うことで画質劣化させます。

result = A.Downscale(scale_min=0.1, scale_max=0.25, interpolation=cv2.INTER_LINEAR, p=1)(image=img)
Original
Original
Downscale(scale_min=0.1, scale_max=0.25)
Downscale(scale_min=0.1, scale_max=0.25)
Downscale(scale_min=0.1, scale_max=0.25, interpolation=cv2.INTER_LINEAR)
Downscale(scale_min=0.1, scale_max=0.25, interpolation=cv2.INTER_LINEAR)
主要パラメータ
scale_min縮小の加減 < 1
scale_max縮小の上限
interpolation補完方法。デフォルトはCV2.INTER_NEAREST。
OpenCVの補完方法が選択できます。

GaussNoise

ガウスノイズをつけます

result = A.GaussNoise(var_limit=(100, 150), p=1)(image=img)
Original
Original
GaussNoise()
GaussNoise()
主要パラメータ
var_limit変動範囲 (min, max)で指定。floatで指定した場合は、(0, val)までになる

ImageCompression

圧縮ノイズを付加します。

result = A.ImageCompression(quality_lower=1, quality_upper=10, p=1)(image=img)
Original
Original
ImageCompression(quality_lower=1, quality_upper=10
ImageCompression(quality_lower=1, quality_upper=10)
主要パラメータ
quality_lower画質の下限。1~100の範囲で指定する
quality_upper画質の上限。1~100の範囲で指定する

InvertImg

画素値を反転させます

result = A.InvertImg(p=1)(image=img)
Original
Original
InvertImg()
InvertImg()

Sharpen

シャープネス処理を施します

result = A.Sharpen(alpha=(0, 1.0), p=1)(image=img)
Original
Original
Sharpen(alpha=(0, 1.0))
Sharpen(alpha=(0, 1.0))
主要パラメータ
alpha(min, max)で設定。0がシャープネスなし、1が最大

Normalize

以下の式で正規化します。主に、モデルに入力する前に正規化するための処理です

img = (img - mean * max_pixel_value) / (std * max_pixel_value)

result = A.Normalize(p=1)(image=img)
主要パラメータ
mean平均値。デフォルトは(0.485, 0.456, 0.406)
std偏差。デフォルトは(0.229, 0.224, 0.225)
max_pixel_valueピクセルの最大値。デフォルトは255.0

ToTensorV2

pytorchのテンソルに変換します。これを利用するには、以下のインポートが必要です。

import albumentations.pytorch

以下のようにpytorchのテンソルに変換できます。

result = A.pytorch.ToTensorV2()(image=img)
result['image'].shape
torch.Size([3, 500, 505])

Blur

Blurとして分類されている機能の一部です

AdvancedBlur

ぼかしフィルタです。詳しくは、https://arxiv.org/abs/2107.10833を参考にしてください。

result = A.AdvancedBlur(
    blur_limit=(3, 15), 
    sigmaX_limit=(0.5, 3), 
    sigmaY_limit=(0.5, 3),  p=1)(image=img)
Original
Original
AdvancedBlur(blur_limit=(3, 15),sigmaX_limit=(0.5, 3),sigmaY_limit=(0.5, 3))
AdvancedBlur(blur_limit=(3, 15),sigmaX_limit=(0.5, 3),sigmaY_limit=(0.5, 3))
主要パラメータ
blur_limitカーネルサイズ。0または奇数
sigmaX_limitx方向のガウスカーネルの標準偏差
sigmaY_limity方向のガウスカーネルの標準偏差
beta_limit1が正規分布。0~の範囲で裾野の形が変わる
rotate_limitガウスカーネルの回転角度 (min,max)で指定。デフォルトは(-90, 90)

Blur

入力画像をぼかす

result = A.Blur()(image=img)
Original
Original
Blur()
Blur()
主要パラメータ
blur_limitカーネルサイズ。3以上を設定する必要がある。デフォルトは(3, 7)

GaussianBlur

ガウスぼかしを行う。ガウスカーネルと偏差が個別に設定できる。

Original
Original
GaussianBlur
GaussianBlur()
主要パラメータ
blur_limitカーネルサイズ。0以上を設定する必要がある。デフォルトは(3, 7)
sigma_limitシグマの範囲をfloatか(min, max) で指定する。
0に設定するとカーネルサイズに合わせて自動計算される。

Crop

Cropとして分類されている機能の一部です

BBoxSafeRandomCrop

bboxを失わずに、クロップします。主に物体検出タスクのデータ拡張で利用します(物体検出については、後で解説します)

bboxesは、物体検知の枠情報です(albumentationフォーマット。他のフォーマットも選択可能)。下記コードには、変換後の枠の描画も含まれています。

bboxes = [[0.01, 0.55 , 1.0,0.94]]

def clamp(n, smallest, largest):
    return max(smallest, min(n, largest))

result = A.BBoxSafeRandomCrop(erosion_rate=0.3)(image=img, bboxes=bboxes)
img2 = result['image'].copy()
for bbox in result['bboxes']:
  x1, y1, x2, y2 = bbox
  h, w, _ = img2.shape
  x1 = clamp(x1, 0, 1)
  y1 = clamp(y1, 0, 1)
  x2 = clamp(x2, 0, 1)
  y2 = clamp(y2, 0, 1)
  cv2.rectangle(img2, (int(x1*w), int(y1*h)), (int(x2*w), int(y2*h)), (0,0,255), 4)

cv2_imshow(img2)
オリジナル(枠付き)
original(枠付き)
BBoxSafeRandomCrop(erosion_rate=0.3)
BBoxSafeRandomCrop(erosion_rate=0.3)

※左図では画像サイズが自動調整されているため、実際の結果とは異なります。

主要パラメータ
erosion_rate枠をどの程度の範囲まで侵食してよいかを指定。
0.0を指定した場合は枠ははみ出させないようにクロップ

CenterCrop

中央部分をトリミングする

result = A.CenterCrop(width=200, height=200)(image=img)
Original
Original
CenterCrop(width=200, height=200)
CenterCrop(width=200, height=200)

※左図では画像サイズが自動調整されているため、実際の結果とは異なります。

主要パラメータ
height切り抜く高さを指定
width切り抜く幅を指定

Crop

画像を指定された領域で切り抜きます

result = A.Crop(x_min=0, y_min=0, x_max=256, y_max=100)(image=img)
Original
Original
Crop(x_min=0, y_min=0, x_max=256, y_max=100)
Crop(x_min=0, y_min=0, x_max=256, y_max=100)

※左図では画像サイズが自動調整されているため、実際の結果とは異なります。

主要パラメータ
x_min(x_min, y_min)-(x_max, y_max)で切り抜き
y_min
x_max
y_max

RandomCrop

ランダムに切り抜きます

result = A.RandomCrop(width=200, height=200)(image=img)
Original
Original
RandomCrop(width=200, height=200)
RandomCrop(width=200, height=200)

※左図では画像サイズが自動調整されているため、実際の結果とは異なります。

主要パラメータ
height切り抜く高さを指定
width切り抜く幅を指定

Dropout

Dropoutとして分類されている機能の一部です

CoarseDropout

長方形領域をドロップアウトします。

result = A.CoarseDropout(
    max_holes= 100,
    max_height=16, 
    max_width =16, p=1)(image=img)
Original
Original
CoarseDropout(max_holes= 100, max_height=16, max_width =16)
CoarseDropout(max_holes= 100, max_height=16, max_width =16)

主要パラメータ
max_holes最大の穴の数
min_holes最小の穴の数
max_height最大の穴の高さ
min_height最小の穴の高さ
max_width最大の穴の幅
min_width最小の穴の幅
fill_value穴を塗りつぶす値。デフォルトは0。floatまたは(0,0,0)形式で指定する

Cutout

矩形領域をドロップアウトします(CoarseDropoutとの差がよくわかりません)

result = A.Cutout (
    num_holes=100, 
    max_h_size=16, max_w_size=16, 
    fill_value=(255,0,0), p=1)(image=img)
Original
Original
Cutout (num_holes=100, max_h_size=16, max_w_size=16, fill_value=(255,0,0))
Cutout (num_holes=100, max_h_size=16, max_w_size=16, fill_value=(255,0,0))

主要パラメータ
num_holes領域の数
max_h_size最大の穴の高さ
max_w_size最大の穴の幅
fill_value穴を塗りつぶす値。デフォルトは0。floatまたは(0,0,0)形式で指定する

幾何変換

Geometricとして分類されている機能の一部です

LongestMaxSize

アスペクト比を維持して、縦横の大きい方がmax_sizeを等しくなるようにスケーリングします

result = A.LongestMaxSize(max_size=256)(image=img)
主要パラメータ
max_size最大サイズ
interpolation補完方法。デフォルトはCV2.INTER_NEAREST。
OpenCVの補完方法が選択できます。

RandomScale

サイズをランダムに変更します

result = A.RandomScale(scale_limit=(-1, 1), p=1)(image=img)
主要パラメータ
scale_limit0を1倍として、(min, max)の範囲で倍率を設定します。
デフォルトは(-0,1. 0.1)です
interpolation補完方法。デフォルトはCV2.INTER_NEAREST。
OpenCVの補完方法が選択できます。

Resize

指定された高さと幅に変更します。アスペクト比は保存されません。

result = A.Resize(width=100, height=100, p=1)(image=img)
主要パラメータ
width幅を指定します
height高さを指定します
interpolation補完方法。デフォルトはCV2.INTER_NEAREST。
OpenCVの補完方法が選択できます。

SmallestMaxSize

アスペクト比を維持して、縦横の小さい方がmax_sizeを等しくなるようにスケーリングします

result = A.SmallestMaxSize(max_size=256)(image=img)
主要パラメータ
max_size最大サイズ
interpolation補完方法。デフォルトはCV2.INTER_NEAREST。
OpenCVの補完方法が選択できます。

その他

その他の変換で気になるやつです。

HistogramMatching

ヒストグラムが一致するように、ピクセル値を変換します。下記では、読み込んだ画像を渡して、read_fnは、画素値そのものを返すように設定しています。

ターゲットドメインが明確な場合のドメイン適応のための機能のようです。

ref = cv2.imread("dark.png")
result = A.HistogramMatching([ref], read_fn=lambda x: x, p=1)(image=img)
Original
Original
Ref
HistogramMatching([ref], read_fn=lambda x: x)
HistogramMatching([ref], read_fn=lambda x: x)
主要パラメータ
reference_images参照画像(リスト形式)。
デフォルトではファイル名を[path]とカッコで括って渡す
read_fnrefrence_imagesの読み込み関数
blend_ratioブレンド比率。0でOriginal、1に近いヒストグラムになる。

RandomFog

霧をシミュレートして描画します

result = A.RandomFog(fog_coef_lower=0.3, fog_coef_upper=1, alpha_coef=0.08, p=1)(image=img)
Original
Original
RandomFog(fog_coef_lower=0.3, fog_coef_upper=1, alpha_coef=0.08)
RandomFog(fog_coef_lower=0.3, fog_coef_upper=1, alpha_coef=0.08)
主要パラメータ
fog_coef_lower霧強度係数の下限。0~1の範囲
fog_coef_upper霧強度係数の上限。0~1の範囲
alpha_coef霧の輪の透明度。0~1の範囲

RandomRain

雨のエフェクトを描画します

result = A.RandomRain(p=1)(image=img)
Original
Original
RandomRain(
RandomRain()
主要パラメータ
slant_lower-20~20の範囲で指定
slant_upper-20~20の範囲で指定
drop_length0~100の範囲で指定
drop_width1~5の範囲で指定
drop_color(r, g, b)で指定
blur_valueボケの長さ(int)
brightness_coefficient0~1で指定
rain_type[None, “drizzle”, “heavy”, “torrential”]の1つ

物体検出のデータに関して

Albumentationsの特徴の1つが、物体検知のBBOXやセグメンテーション情報も同時に加工してくれるところです。ここでは、物体検出でAlbumentationsを利用する場合について解説します。

BBOXのフォーマット

Albumentationでは、pascal_voc, albumentationsオリジナル、coco, yoloのBBOXフォーマットをサポートしています。

以下、それぞれのフォーマットです。

参照元:https://albumentations.ai/docs/getting_started/bounding_boxes_augmentation/

フォーマットの指定方法

Composeのオプションで、どのフォーマットを選択するか設定できます。例えば、yoloフォーマットを利用する場合は、以下のようにbbox_paramsに指定します。

transform = A.Compose([
    A.RandomCrop(width=256, height=256, p=1),
    A.HorizontalFlip(p=0.5),
    A.RandomBrightnessContrast(p=0.2),
], bbox_params=A.BboxParams(format='yolo'))

このとき、min_area, min_visibilityを設定することができます。

#ex1)
transform = A.Compose([
    A.RandomCrop(width=256, height=256, p=1),
    A.HorizontalFlip(p=0.5),
    A.RandomBrightnessContrast(p=0.2),
], bbox_params=A.BboxParams(format='yolo', min_area=256))

#ex2)
transform = A.Compose([
    A.RandomCrop(width=256, height=256, p=1),
    A.HorizontalFlip(p=0.5),
    A.RandomBrightnessContrast(p=0.2),
],bbox_params=A.BboxParams(format='yolo', min_visibility=0.25))

それぞれ、以下の意味になります。

min_area BBOXの面積が指定された面積より小さくなった場合に、そのBBOXを削除する

min_visibility 元の面積に対して、BBOXの面積が指定した比率より小さくなった場合に削除

ex1は面積が256ピクセル以下になった場合、ex2は面積が25%以下になった場合にBBOXが削除されます。

変換へのBBOXの渡し方

変換へのBBOXの渡し方は簡単です。

YOLOフォーマットの場合、BBOXは以下のような形式で格納します。

bboxes = [
  [0.1, 0.1, 0.2, 0.2],
  [0.3, 0.4, 0.1, 0.2],
    :
]

これを画像と一緒に渡します。

image = cv2.imread('/path/to/image')

transform = A.Compose([
    A.RandomCrop(width=256, height=256, p=1),
    A.HorizontalFlip(p=0.5),
    A.RandomBrightnessContrast(p=0.2),
], bbox_params=A.BboxParams(format='yolo'))

result = transform(imgas=image, bboxes=bboxes)

出力は、result[‘image’]に変換後の画像が、result[‘bboxes’]に変換後のBBOXの情報が格納されます。

クラス名を渡したい場合は、以下のようにすればクラス名を渡すことができます。この場合、戻り値のbboxesには、5つ目の引数が追加されています。なお、”car”などのラベル名ではなく、101などのIDを渡しても良いです。

def clamp(n, smallest, largest):
    return max(smallest, min(n, largest))
bboxes = [[0.01, 0.55 , 1.0,0.94, "car"]]

result = A.BBoxSafeRandomCrop(erosion_rate=0.3)(image=img, bboxes=bboxes)
img2 = result['image'].copy()
for bbox in result['bboxes']:
  x1, y1, x2, y2, label = bbox
  print(x1, y1, x2, y2, label)
  h, w, _ = img2.shape
  x1 = clamp(x1, 0, 1)
  y1 = clamp(y1, 0, 1)
  x2 = clamp(x2, 0, 1)
  y2 = clamp(y2, 0, 1)
  cv2.rectangle(img2, (int(x1*w), int(y1*h)), (int(x2*w), int(y2*h)), (0,0,255), 4)

cv2_imshow(img2)

実験した感じでは、bboxesの各要素の先頭から4つだけ(上のコードの場合[0.01, 0.55 , 1.0, 0.94, “car”]の赤文字の部分だけ)が変換対象で、残りの部分は変更されずに戻ってくる仕様になっているようです。

まとめ

以上、データ拡張用のライブラリAlbumentationsの簡単な使い方について説明しました。ここに記載している機能以外にもたくさんのデータ拡張が用意されていますので、公式サイトもチェックしてみてください。

おすすめ書籍

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

記事URLをコピーしました