背景
前回物体検出のモデルであるSSDを紹介しました。
今回物体検出の一つモデルRetinaNetを紹介したいと思います
(本文書はこちらのソースコード https://github.com/yhenon/pytorch-retinanet を参考に整理したものです)
本節はデータの前処理を紹介いたします。
データの種類
二つがあります
1.画像
image(H,W,C)
2.目標物体のカテゴリーと範囲を表す座標(Annotations)
cx1,cy1,w1,h1,category1
データ前処理の流れ
1. Annotationsの前処理
cx_start,cy_start,width,height
から
cx_start,cy_start,cx_end,cy_end
まで
変更します
図のように、変更されたAnnotationsは左上の開始点と右下の終了点になります。
2. 画像の前処理
図のより、もともとの画像は次元が(H,W,C)のTensorです
以下の四つのスッテプを経由します。最後のOutputはモデルのinputとしてモデルに入れます。
1. /255処理
2. Normalizaer処理
3. Argumenter処理
4. Resize処理
2-1. /255処理
図のように、画像データは次元が(H,W,C)のTensorです。各要素は0から255までの値です。
多分、計算量を減らすため、0から255の値を最大値255に割り算して、0から1までの値に変更します。
2-2. Normalizaer処理
図のように、Cチャンネル画像データの各要素を事前に定められた該当するmeanと引き算して、事前に定められたstdと割り算します。
3. Argumenter処理
データの多様性を保つために、データの水増しが行います。今回は0.5の確率で画像を横逆転します。
そして、関連するAnnotation情報も左右逆転します。
図のようにAnnotation情報も左右逆転します。感覚的には、開始点を左上から右上に移動したり、終了点を右下から左下に移動します。
4. Resize処理
画像データはさまざまがあります。それは入力された画像のサイズによります。
640*480
1024*640
などいろいろサイズがあります。
4-1. Resizeされるサイズの条件
それらのサイズを以下の条件を満たすように変形します
短い辺の長さを608、もしくは長い辺の長さを1024にします
短い辺の長さを最長608、長い辺の長さを最長1024のサイズに変形します。
4-2. 横幅が小さい場合ケースA
図のように、予め定められた横幅が608,縦幅が1024の基準ボックスがあります。まず画像データの横幅を基準ボックスの横幅に当てはまってみって、画像データの縦幅を同比例に変形します。
変形された縦幅は基準ボックスの縦幅より小さいなら、変形はそのまま成功になります。
scale = 基準ボックスの横幅/画像データの横幅
4-3. 横幅が小さい場合ケースB
4-1とは別に、横幅を当てはまってみて、縦幅は基準ボックスの縦幅よりおおきいなら、縦幅を嵌ってみます。
scale = 基準ボックスの縦幅/画像データの縦幅
4-4. 縦幅が小さい場合ケースA
図のように、画像データの縦幅は横幅とくらべたら、小さい場合。予め定められた横幅が1024,縦幅が608の基準ボックスがあります。
先に縦幅を608に当てはまってみます。算出された横幅が基準ボックスの縦幅1024より小さいなら、そのままにします。
4-5. 縦幅が小さい場合ケースB
図のように、画像データの縦幅は横幅とくらべたら、小さい場合。予め定められた横幅が1024,縦幅が608の基準ボックスがあります。
先に縦幅を608に当てはまってみます。算出された横幅が基準ボックスの縦幅1024より大きいです。それはだめですので、次に横幅を1024に当てはまめます。
4-6. AnnotationsのResize処理
図のように、画像データのResizeが行うとともに、AnnotationsのResizeも行う必要があります。具体的には4-1、4-2から計算されたscaleとAnnotations掛け算します。
4-7. Padding処理
最後に、Padding処理を行います。現在の縦幅と横幅を32個の1X1の要素を入れようとします。
どれぐらい1X1の要素を入れるのは横幅と縦幅から32とのMOD計算の結果によります。
padding_w = w + 32 - w % 32
padding_h = h + 32 - h % 32
ソースコード
test_image_flip
def test_image_flip(path):
image = skimage.io.imread(path) # (H,W,C)
b = image.astype(np.float32) / 255.0
skimage.io.imshow(b)
skimage.io.show()
c = b[:, ::-1, :]
skimage.io.imshow(c)
skimage.io.show()
test_normalization
def test_normalization(path):
image = skimage.io.imread(path) # (H,W,C)
image = image.astype(np.float32) / 255.0
skimage.io.imshow(image)
skimage.io.show()
mean = np.array([[[0.485, 0.456, 0.406]]])
std = np.array([[[0.229, 0.224, 0.225]]])
transformedImage = (image.astype(np.float32) - mean) / std
skimage.io.imshow(transformedImage)
skimage.io.show()
test_resize
def test_resize(path):
smallerLineMaxLength = 608
largerLineMaxLength = 1024
image = skimage.io.imread(path)
skimage.io.imshow(image)
skimage.io.show()
imageWidth = image.shape[1]
imageHeight = image.shape[0]
matchMin = min(imageWidth, imageHeight)
matchMax = max(imageWidth, imageHeight)
scale = smallerLineMaxLength / matchMin
resizedMax = int(matchMax * scale)
if resizedMax > largerLineMaxLength:
scale = largerLineMaxLength / matchMax
resizedWidth = int(imageWidth * scale)
resizedHeight = int(imageHeight * scale)
trasformedImage = skimage.transform.resize(image, (resizedHeight, resizedWidth))
skimage.io.imshow(trasformedImage)
skimage.io.show()
paddingHeight = 32 - int(resizedHeight) % 32
paddingWidth = 32 - int(resizedWidth) % 32
newImageStruc = np.zeros((resizedHeight + paddingHeight, resizedWidth + paddingWidth, 3))
print(newImageStruc.shape)
newImageStruc[:resizedHeight, :resizedWidth, :] = trasformedImage
skimage.io.imshow(newImageStruc)
skimage.io.show()
まとめ
今回RetineNet第一節はデータの前処理を紹介しました
次回はモデルを詳しく紹介します