endaaman.com

2021-10-09

Tips

Albumentationsで左右対称の画像のbboxを扱うとき、HorizontalFlipで反転したラベルをさらに反転させる

ReplayComposeを使ってHorizontalFlipが適用されたかどうか追跡する

タイトルをそのまま読むと「何のことじゃ」と思われるかもしれないが、bboxでobject detectionする際、解析対象が左右対象だったとき、たとえばleft_xxxright_xxxというラベルが存在した場合、 左右反転するとleft_xxxとなってしまったラベルがright_xxxであるべき(逆もしかり)という状態が存在する。

たとえば、左目と右目のラベルがあったとき、反転が適応されたら鏡写しの左目には右目のラベルが付いているべきという話である。タスクとして、左右の是非を判断する必要がない場合にこの処理があると都合が良い。

keypointsのばあいはAlbumentations Experimental Transforms (augmentations.transforms) - Albumentations Documentationというものもあり、顔のパーツの検出などはこちらを使えばよい。ただしこれはkeypoints限定でbboxでは動作しない。

ReplayComposeを使う

ReplayComposeを使うことで、出力にどのtransformが使われたかを調べることができる。

import warnings
import numpy as np
from torchvision.utils import draw_bounding_boxes
from torchvision.transforms.functional import to_pil_image
import albumentations as A
from albumentations.pytorch.transforms import ToTensorV2


albu = A.ReplayCompose([
    A.HorizontalFlip(p=0.5),
], bbox_params=A.BboxParams(format='pascal_voc', label_fields=['labels']))

horizontal_filpper_index = None
for i, t in enumerate(albu):
    if isinstance(t, A.HorizontalFlip):
        horizontal_filpper_index = i

上の例ではHorizontalFlipが何番目にあるか予め保存しておく。これをすることで後からまたどこにあるか調べる処理を省ける。

image = np.zeros([200, 200, 3], dtype=np.uint8)
bboxes = np.array([[10, 10, 40, 40], [159, 20, 189, 50]])    # PASCAL VOC形式はleft, top, right, bottomの並びで、単位はピクセル
labels = np.array([0, 1])   # 右目:0, 左目:1 と想定する

def visualize(img, bb, ll, path):
    label_to_str = {
        0: 'right',
        1: 'left',
    }
    t = torch.from_numpy(img).permute(2, 0, 1)
    t = draw_bounding_boxes(image=t, boxes=torch.from_numpy(bb), labels=[label_to_str[int(l)] for l in ll])
    img = to_pil_image(t)
    img.save(path)

visualize(image, bboxes, labels, f'tmp/org.png')

出力された画像は以下のようなもの

org.png

左側にleft、右にrightがある。左目は垂れ下がっていることを覚えていただきたい。

このデータに対してtransformを実行する。

transformを実行

result = albu(
    image=image,
    bboxes=bboxes,
    labels=labels,
)

bboxes = np.array(result['bboxes'])
labels = np.array(result['labels'])
print(bboxes)
print(labels)
if horizontal_filpper_index is not None:
    flipped = result['replay']['transforms'][horizontal_filpper_index]['applied']
    if flipped:
        print('flipped')
        # ラベルを再反転させる。今回は0と1を入れ替える
        labels *= -1
        labels += 1

visualize(image, bboxes, labels, f'tmp/flipped.png')

反転が起きるとこのような画像が出てくる。

flipped.png

垂れ下がった本来左目であったbboxは右側にあり、右目のラベルが付いている

付記

bboxについても需要はあるようなので(Propose to save used parameters and was transform applied or not as a parameter · Issue #381 · albumentations-team/albumentations)、keypointsのものがexprimentalが取れてそのうち本体に組み込まれる際に、bboxもサポートされる可能性はある。


©2024 endaaman.com