こしあん
2018-10-25

Python(Numpy)で画像を水平反転する方法:Data Augmentation向け

Pocket
LINEで送る
Delicious にシェア

5k{icon} {views}



OpenCVを使わずに単純に画像を左右反転(水平反転)する方法を考えます。ディープラーニングでデータのジェネレーターを自分で実装した場合、Data Augmentationを組み込む際にも必要になります。それを見ていきましょう。

左右反転自体は実は簡単

例えばNumpyの行列を左右反転させてみましょう。実はこれだけでOKです。

>>> x = np.arange(16).reshape(4,4)
>>> x
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])
>>> x[:, ::-1]
array([[ 3,  2,  1,  0],
       [ 7,  6,  5,  4],
       [11, 10,  9,  8],
       [15, 14, 13, 12]])

スライス記法の「::-1」が反転を示すので、反転したい軸でこれを使えばいいだけです。ただし物体検出などでは、付随するラベルを注意深く変換しないといけないのでそこが注意が必要です。

具体例を見てみましょう。猫の画像をcat.jpgとして用意しました。かわいいですね。

これを左右反転してみます。読み込みのときだけPillowのライブラリを使っていますがそれ以外はNumpyでできます。

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

if __name__ == "__main__":
    img = np.asarray(Image.open("cat.jpg"), dtype=np.uint8)
    img = img[:, ::-1, :]
    plt.imshow(img)
    plt.savefig("cat_reverse.jpg")

まずは読み込んでNumpy配列に変換し、このNumpy配列が「y, x, color」の順で記録されているので、x軸について反転させ、あとはpyplotで表示させているだけです。

結果はこのようになります。左右反転できていますね。

アノテーションや境界箱(Bounding Box)の変換を忘れずに

特に物体検出などで必要になるのですが、画像を左右反転するだけではなくてラベルのほうも変換する必要があります。アノテーションというのは例えば顔のランドマーク検出だったら、目の位置や口の位置の座標。境界箱(Bounding Box)というのは、物体検出のここに何があるという領域(よく四角で囲まれるやつ)ですね。ここでは境界箱の例で説明します。

いま座標系を左上を(0, 0)として右下を(1, 1)としましょう。先程の猫の画像に、(x, y)の順で左上=(0.3, 0.1)、右下=(0.8, 0.95)なる境界箱を合成してみます。以下のコードです。

def draw_bbox(image_array, bbox, lwd=5):
    assert image_array.dtype == np.uint8
    assert min(bbox) >= 0.0 and max(bbox) <= 1.0
    assert len(bbox) == 4
    height, width = image_array.shape[0], image_array.shape[1]
    sx, sy, ex, ey = bbox
    sx, ex = int(sx*width), int(ex*width)
    sy, ey = int(sy*height), int(ey*height)

    img = np.copy(image_array)
    filter = np.zeros(image_array.shape, dtype=np.bool)
    filter[sy:(ey+lwd), sx:(ex+lwd), 0] = True
    filter[(sy+lwd):ey, (sx+lwd):ex, 0] = False
    img[filter] = 255 # red
    return img

if __name__ == "__main__":
    img = np.asarray(Image.open("cat.jpg"), dtype=np.uint8)
    bbox = [0.3, 0.1, 0.8, 0.95]
    merged_img = draw_bbox(img, bbox)
    plt.imshow(merged_img)
    plt.show()

draw_bboxの関数の前半は0~1の座標系を、幅と高さから元の座標系に戻している操作。後半は四角形を描画するための処理です。Pillowとかでも書けますがせっかくなのでNumpyだけで書きました。後半は説明のためのコードなので気にしなくていいです。

このように合成できました。さてここからがポイント。今この境界箱を書くのに指定したのは、左上と右下の座標(2点×xy=4つのパラメーター)です。左上と右下の点を水平反転するとどの座標に移動するかわかりますか?

ぱっと思い浮かぶのは、左上と右下のx座標を1から引く方法。確かにこれだと反転はします。自分もしばらくこれだけでいいと勘違いしてたのですが、x座標を1から引くだけでは半分正解で半分不正解です。実はまだあります。よく考えてください。

水平反転とは画像の右に鏡を置いてその鏡像を取り出すのと同じなので、元の画像の左上の点は鏡の中では右上に、元の画像の右下の点は鏡の中では左下に移動します。つまり、座標という数値上の変換だけではなく、どこがどこに移動するかそれに伴ってアノテーションはどうかわるのかという意味上の変換も必要です。例えばランドマーク検出だったら、左目の点は右目の点に、右目の点は左目の点にラベルをスワップさせてあげないといけません。そこを忘れないでください。

さてそれを加味すると、境界箱の水平反転を含めた画像の変換は次のようになります。

def horizontal_flip(img_array, bbox):
    assert min(bbox) >= 0.0 and max(bbox) <= 1.0
    assert len(bbox) == 4
    flipped_image = img_array[:, ::-1, :]
    flipped_bbox = [1-bbox[2], bbox[1], 1-bbox[0], bbox[3]]
    return flipped_image, flipped_bbox

境界箱の場合は、x座標を1から引くだけではなく、左上と右下のx座標を入れ替えるのが正解です。これをプロットすると次のようになります。

これで良いですね。

Data Augmentationでの水平反転(Horizontal flip)

最後にディープラーニングでの画像の反転に意味づけについて補足しておきます。英語ではHorizontal flipなどと呼ばれる手法で、ディープラーニングにおける典型的なData Augmentationの方法です。訓練時のみ使い、50%の確率で左右反転する方法です。

水平反転はよく使いますが垂直反転はあまり使いません。左を向いた猫と右を向いた猫はよく見ても、上下逆さまの猫はまず見ないですからね。

さて、この乱数部分ですが、numpyではなく組み込みのrandomライブラリを使うのがスマートそうです。random.random()で0~1の乱数が1個出てくるので0.5より大きいか小さいかで判定できます。

import random

def random_horizontal_flip(img_array, bbox):
    if random.random() > 0.5:
        return horizontal_flip(img_array, bbox)
    else:
        return img_array, bbox

とてもスマートに実装できますね。呼び出し部分です。

if __name__ == "__main__":
    img = np.asarray(Image.open("cat.jpg"), dtype=np.uint8)
    bbox = [0.3, 0.1, 0.8, 0.95]
    img, bbox = random_horizontal_flip(img, bbox)
    merged_img = draw_bbox(img, bbox)
    plt.imshow(merged_img)
    plt.show()

文字通り乱数なので、実行したタイミングによって結果が変わると思います。何回か実行してみてください。

まとめ

まとめます。

  • 画像の反転は、反転したい軸を「::-1」というスライス記法で簡単に反転できる。単体の画像のNumpy配列の場合は、「y, x, c」という構成なので、2番目の軸を反転すると水平反転になる。
  • 物体検出やランドマークの場合、アノテーションの変換をお忘れずに。1からx座標を引くという数値上の変換だけではなく、どのアノテーションがどこに移動するのかという意味上の変換がポイント。

以上です。アノテーションの変換はなかなか忘れがちなポイントですが、面倒でも一度簡単な例に落とし込んで試してみるとデバッグが捗ると思います。



Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内

技術書コーナー

【新刊】インフィニティNumPy――配列の初期化から、ゲームの戦闘、静止画や動画作成までの221問

「本当の実装力を身につける」ための221本ノック――
機械学習(ML)で避けて通れない数値計算ライブラリ・NumPyを、自在に活用できるようになろう。「できる」ための体系的な理解を目指します。基礎から丁寧に解説し、ディープラーニング(DL)の難しいモデルで遭遇する、NumPyの黒魔術もカバー。初心者から経験者・上級者まで楽しめる一冊です。問題を解き終わったとき、MLやDLなどの発展分野にスムーズに入っていけるでしょう。

本書の大きな特徴として、Pythonの本でありがちな「NumPyとML・DLの結合を外した」点があります。NumPyを理解するのに、MLまで理解するのは負担が大きいです。本書ではあえてこれらの内容を書いていません。行列やテンソルの理解に役立つ「従来の画像処理」をNumPyベースで深く解説・実装していきます。

しかし、問題の多くは、DLの実装で頻出の関数・処理を重点的に取り上げています。経験者なら思わず「あー」となるでしょう。関数丸暗記では自分で実装できません。「覚える関数は最小限、できる内容は無限大」の世界をぜひ体験してみてください。画像編集ソフトの処理をNumPyベースで実装する楽しさがわかるでしょう。※紙の本は電子版の特典つき

モザイク除去から学ぶ 最先端のディープラーニング

「誰もが夢見るモザイク除去」を起点として、機械学習・ディープラーニングの基本をはじめ、GAN(敵対的生成ネットワーク)の基本や発展型、ICCV, CVPR, ECCVといった国際学会の最新論文をカバーしていく本です。
ディープラーニングの研究は発展が目覚ましく、特にGANの発展型は市販の本でほとんどカバーされていない内容です。英語の原著論文を著者がコードに落とし込み、実装を踏まえながら丁寧に解説していきます。
また、本コードは全てTensorFlow2.0(Keras)に対応し、Googleの開発した新しい機械学習向け計算デバイス・TPU(Tensor Processing Unit)をフル活用しています。Google Colaboratoryを用いた環境構築不要の演習問題もあるため、読者自ら手を動かしながら理解を深めていくことができます。

AI、機械学習、ディープラーニングの最新事情、奥深いGANの世界を知りたい方にとってぜひ手にとっていただきたい一冊となっています。持ち運びに便利な電子書籍のDLコードが付属しています。

「おもしろ同人誌バザールオンライン」で紹介されました!(14:03~) https://youtu.be/gaXkTj7T79Y?t=843

まとめURL:https://github.com/koshian2/MosaicDeeplearningBook
A4 全195ページ、カラー12ページ / 2020年3月発行

Shikoan's ML Blog -Vol.1/2-

累計100万PV超の人気ブログが待望の電子化! このブログが電子書籍になって読みやすくなりました!

・1章完結のオムニバス形式
・機械学習の基本からマニアックなネタまで
・どこから読んでもOK
・何巻から読んでもOK

・短いものは2ページ、長いものは20ページ超のものも…
・通勤・通学の短い時間でもすぐ読める!
・読むのに便利な「しおり」機能つき

・全巻はA5サイズでたっぷりの「200ページオーバー」
・1冊にたっぷり30本収録。1本あたり18.3円の圧倒的コストパフォーマンス!
・文庫本感覚でお楽しみください

北海道の駅巡りコーナー

日高本線 車なし全駅巡り

ローカル線や秘境駅、マニアックな駅に興味のある方におすすめ! 2021年に大半区間が廃線になる、北海道の日高本線の全区間・全29駅(苫小牧~様似)を記録した本です。マイカーを使わずに、公共交通機関(バス)と徒歩のみで全駅訪問を行いました。日高本線が延伸する計画のあった、襟裳岬まで様似から足を伸ばしています。代行バスと路線バスの織り成す極限の時刻表ゲームと、絶海の太平洋と馬に囲まれた日高路、日高の隠れたグルメを是非たっぷり堪能してください。A4・フルカラー・192ページのたっぷりのボリュームで、あなたも旅行気分を漫喫できること待ったなし!

見どころ:日高本線被災区間(大狩部、慶能舞川橋梁、清畠~豊郷) / 牧場に囲まれた絵笛駅 / 窓口のあっただるま駅・荻伏駅 / 汐見の戦争遺跡のトーチカ / 新冠温泉、三石温泉 / 襟裳岬

A4 全192ページフルカラー / 2020年11月発行


Pocket
LINEで送る
Delicious にシェア

Add a Comment

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