はじめに
ディープラーニングで学習画像を取得する場合、いろいろな角度から画像を取得することで学習精度を上げるケースがあります。このとき画像をいろいろな角度で撮り直すのが面倒なので、画像処理で学習データの水増しを行うことで画像撮影作業の効率化を検討してみました。
ちなみに、学習データの水増し手法として、ノイズを増やす、コントラストを変える、明るさを変える、平滑化、拡大縮小、反転(左右/上下)、回転、シフト(水平/垂直)、部分マスク、トリミング、変形、変色、背景の変更などがあり利用しているディープラーニングライブラリによっては水増し機能が用意されているケースがありますので、環境によってはここまで自作する必要は無いかもしれません。
なお、少ない画像データで水増しして学習させている場合、特定のデータでのみ認識する過学習が発生する可能性がありますので、このツールはPoCで利用するレベルにしておくなど、あくまで参考程度としてください。
処理概要
アフィン変換を用いて与えられた画像を指定した角度づつ元画像の中心を軸に回転させて画像を保存します。このとき画像の縦横サイズが最大となるように余白をとります。
パラメータを指定しないで data/maxgogo.jpg に画像を置いて実行すると、data以下に maxgogo_0.jpg ~maxgogo_xx.jpg が作成されます。
パラメータに画像ファイルのパスを指定すると画像が存在するディレクトリに 画像名_0.jpg ~ 画像名_xx.jpgが作成されます。
この回転画像に合わせたYolo用のアノテーションデータ水増しツールは余裕があれば後日公開するかな…?
利用例
■引数無しの場合
data/maxgogo.jpg というファイルを保存し
python img_rotation.py
を実行する。
■引数アリの場合
python img_rotation.py 画像ファイル
ソースコード
# -*- coding: utf-8 -*-
import numpy as np
import cv2
import sys
import os
args = sys.argv
print("args=",args)
print(len(args))
if len(args) == 1:
# パラメータを指定しない場合は、data/maxgogo.jpg というファイルが存在する前提です
img_path = "data"
img_name_body = "maxgogo"
img_name_extension = ".jpg"
img_name = img_name_body + img_name_extension
elif len(args) == 2: # 画像ファイルが指定される前提で細かいチェックしてません。
base_dir_pair = os.path.split(args[1])
img_path = base_dir_pair[0]
root_ext_pair = os.path.splitext(base_dir_pair[1])
img_name_body = root_ext_pair[0]
img_name_extension = root_ext_pair[1]
img_name = img_name_body + img_name_extension
print(base_dir_pair)
print("img_path=",img_path)
print(root_ext_pair)
print("img_name_body=",img_name_body)
print("img_name_extension=",img_name_extension)
else :
print("error path/filename")
# パスが無い場合を考慮した処理
if len(img_path) == 0:
# 画像読み込み
img = cv2.imread(img_name)
else:
# 画像読み込み
img = cv2.imread(img_path + os.sep + img_name)
if img is None:
print("**************************")
print("Failed to load image file.")
print("**************************")
sys.exit(1)
print(type(img))
print("size:",img.size)
h, w = img.shape[:2]
size = (w, h)
########################################################
# 回転角の指定 30 だと30度ごと。適当に変更してください。
########################################################
angle = 30
# 取得数 360度を回転角で割る
range_num = 360 // angle
# 指定した角度で回転した場合の最大の幅と高さを取得する
w_max = 0
h_max = 0
# 高さ、幅の最大値を取得
# ragne は 0 から始まり、指定した数-1 まで実行する。
for num in range(range_num):
angle_num = angle*num
angle_rad = angle_num/180.0*np.pi
# 回転後の画像サイズを計算
w_rot = int(np.round(h*np.absolute(np.sin(angle_rad))+w*np.absolute(np.cos(angle_rad))))
h_rot = int(np.round(h*np.absolute(np.cos(angle_rad))+w*np.absolute(np.sin(angle_rad))))
if w_rot > w_max :
w_max = w_rot
if h_rot > h_max :
h_max = h_rot
# 画像書き出し
for num in range(range_num):
angle_num = angle*num
angle_rad = angle_num/180.0*np.pi
size_rot = (w_max, h_max)
# 元画像の中心を軸に回転する
center = (w/2, h/2)
scale = 1.0
rotation_matrix = cv2.getRotationMatrix2D(center, angle_num, scale)
# 平行移動を加える (rotation + translation)
affine_matrix = rotation_matrix.copy()
affine_matrix[0][2] = affine_matrix[0][2] -w/2 + w_max/2
affine_matrix[1][2] = affine_matrix[1][2] -h/2 + h_max/2
# アフィン変換
img_rot = cv2.warpAffine(img, affine_matrix, size_rot, flags=cv2.INTER_CUBIC)
# パスが無い場合を考慮した処理
if len(img_path) == 0:
writefile_name = img_name_body + "_" + str(num) + img_name_extension
else:
writefile_name = img_path + os.sep + img_name_body + "_" + str(num) + img_name_extension
print (writefile_name)
cv2.imshow(writefile_name, img_rot)
cv2.imwrite(writefile_name, img_rot)
cv2.waitKey(0)
実行結果
アニメーションGIF化
参考
https://qiita.com/ryokomy/items/0d1a879cac59a0bfbdc5
を参考に変更させていただきました。