[Amazon SageMaker] Amazon SageMaker Ground Truth で作成したデータをOpenCVで増幅してみました

2020.04.22

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

CX事業本部の平内(SIN)です。

オブジェクト検出では、大量のデータセットが必要ですが、これを準備する作業量は、結構大変です。そのため、データ(画像)を増幅してデータ数を増加させる手法があります。

今回は、OpenCVによる画像処理で、Amazon SageMaker Ground Truth(以下、Ground Truth)で作成したデータを増幅してみました。

なお、画像の回転や、反転などを行うと、Ground Truthで設定したアノテーションを、やり直す必要が生じるので、ここでは、アノテーションが変化しない変換だけを対象にしました。

2 変換ファンクション

試した変換の種類について列挙します。下記が、サンプルのために使用した、800 × 600の元画像です。

(1) 彩度

彩度は、BGR形式を、一旦、HSV形式に変換して変換しています。パラメータに1.0を与えると無変換となります。

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('./test.jpg')

# 彩度
def saturation(src, saturation):
# 一旦、BGRをHSVに変換して彩度を変換する
img = cv2.cvtColor(src,cv2.COLOR_BGR2HSV)
img[:,:,(1)] = img[:,:,(1)] * saturation
img = cv2.cvtColor(img,cv2.COLOR_HSV2BGR)
return img

# 表示
def view(img, func, params):
w = 5 # 横に5つ並べる
h = int(len(params)/w)+1
fig = plt.figure(figsize=(10, 7))
for i,p in enumerate(params):
dst = func(img, p)
ax = fig.add_subplot(h, w, i + 1)
ax.set_axis_off()
ax.imshow(cv2.cvtColor(dst, cv2.COLOR_BGR2RGB)) # BGR => RGB
ax.set_title(f"param={p:.1f}")
plt.show()

view(img, saturation, [0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6,1.8,2.0,3.0])

パラメータは、0.4〜3.0ぐらいが変換として使用できそうです。

下記は、0.2での例です。

3.0での例です。

(2) 明度

明度についても、HSV形式に変換して変換しています。パラメータに1.0を与えると無変換となります。

# 明度
def brightness(src, brightness):
# 一旦、BGRをHSVに変換して明度を変換する
img = cv2.cvtColor(src,cv2.COLOR_BGR2HSV)
img[:,:,(2)] = img[:,:,(2)] * brightness
img = cv2.cvtColor(img,cv2.COLOR_HSV2BGR)
return img

パラメータは、0.4〜0.8ぐらいが変換として使用できそうです。

0.6での変換例です。

(3) コントラスト

各色相をalphaで演算しています。パラメータに1.0を与えると無変換となります。

# コントラスト
def contrast(src, alpha):
# 各色相をalphaで演算する
img = alpha * src
return np.clip(img, 0, 255).astype(np.uint8)

パラメータは、0.6〜2.0ぐらいが変換として使用できそうです。

0.8で変換

2.0で変換

(4) モザイク

全体をリサイズして戻すことでモザイクにしています。パラメータに1.0を与えると無変換となります。 実質的に、ピクセル数の小さなデータセットとなるため、小さく映り込む物体の検出用に有効でかも知れません。なお、パラメータは、想定する検出状況と元データの解像度を考慮して決定する必要があります。

# モザイク
def mosaic(src, ratio):
# ratio倍でリサイズして戻す
img = cv2.resize(src, None, fx=ratio, fy=ratio, interpolation=cv2.INTER_NEAREST)
return cv2.resize(img, src.shape[:2][::-1], interpolation=cv2.INTER_NEAREST)

今回の例となっている800 × 600 では、パラメータ0.05ぐらいまでが有効になりそうです。

0.1で変換したものです。

(5) ガウスノイズ

ガウスノイズです。パラメータに1.0を与えると無変換となります。

# ガウスノイズ
def gaussian(src, sigma):
# ランダム値で生成した画像と合成する
row,col,ch = src.shape
mean = 0
gauss = np.random.normal(mean, sigma, (row, col, ch)).astype('u1')
gauss = gauss.reshape(row, col, ch)
return src + gauss

有効なパラメータは、10〜100ぐらいでしょうか。

20での変換例です。

(6) ごま塩ノイズ

ランダム値で生成した画像を合成しています。

# ごま塩ノイズ
def noise(src, amount):
# ランダム値で生成したノイズと合成する
img = src.copy()
num_pepper = np.ceil(amount* src.size * (0.5))
coords = [np.random.randint(0, i-1 , int(num_pepper)) for i in src.shape]
img[coords[:-1]] = (0,0,0)
return img

パラメータ0.1で変換した例です。

3 増幅

ここまでの変換用ファンクションを使用して、Ground Truth のデータを増幅しているコードです。 変換リストに、ファンクション名とパラメータをセットすることで増幅の内容を指定できます。

amplify.py

def main():

# 変換リスト
convertList = [
{"function": saturation, "param": 0.2}, # 彩度
{"function": saturation, "param": 3.0},
{"function": brightness, "param": 0.6}, # 明度
{"function": contrast, "param": 0.8}, # コントラスト
{"function": mosaic, "param": 0.05},# モザイク
{"function": mosaic, "param": 0.1},
{"function": mosaic, "param": 0.2},
{"function": gaussian, "param": 20.0},# ガウスノイズ
{"function": noise, "param": 0.1}, # ごま塩ノイズ
]

# 元データの情報を取得する
dataList = getDataList(targetPath, manifest)
print("全データ: {}件 ".format(len(dataList)))

outputManifest = ''
for data in dataList:
# 元データのエクスポート
outputManifest += data.dumps() + '\n'
# 元データの画像
srcImage = "{}/{}.{}".format(targetPath, data.baseName, data.ext)

orgBaseName = data.baseName
# 変換リストに基づく増幅処理
for i,convert in enumerate(convertList):
# 元データの名前取得
baseName = "{}-{}".format(orgBaseName, str(i+1).zfill(3))
# 出力データの画像ファイル名
dstImage = "{}/{}.{}".format(targetPath, baseName, data.ext)
# 元データの名前を変更
data.baseName = baseName
# 変換データのエクスポート
outputManifest += data.dumps() + '\n'
# 画像の変換処理
img = cv2.imread(srcImage)
img = convert["function"](img, convert["param"])
# 変換後の画像のエクスポート
cv2.imwrite(dstImage,img)
# Manifestファイルのエクスポート
with open("{}/{}".format(targetPath, manifest), mode='w') as f:
f.write(outputManifest)

print("増幅後データ: {}件 ".format(len(outputManifest.split('\n'))))

main()

実行すると、下記のように4件のデータが約10倍になっていることを確認できます。

$ python3 amplify.py
全データ: 4件
増幅後データ: 41件

4 最後に

今回は、Ground Truthのデータを増幅してみました。この変換では、改めてアノテーションする作業は発生しないので、作業時間は、殆ど必要ないでしょう。 なお、データの増幅は、最終的なオブジェクト検出の条件を考慮して、注意深く行う必要があると思います。

すべてのコードは、下記に起きました。

https://gist.github.com/furuya02/6b10263c77d0b62b492f96f107d38dc7

5 参考にさせて頂いたページ

OpenCV - 画像の明るさやコントラストを変更、ガンマ補正など

機械学習のデータセット画像枚数を増やす方法