アプリとサービスのすすめ

アプリやIT系のサービスを中心に書いていきます。たまに副業やビジネス関係の情報なども気ままにつづります

衛星のSAR画像-セグメンテーションコンペの備忘録

衛星データのSAR画像を用いたセグメンテーションのコンペがあったので、その際の使ったコードとか手法の備忘録。

コンペ内容は事情により省略。手法だけまとめてきます。

大雑把に言うと、過去と現在の画像から特定の領域を0, 1でセグメンテーションするタスク。

目次
1. 使ったネットワーク「HRNet」
2.グレースケールのtifフォーマット画像の読み込み
3.tifフォーマット画像のrgb化
4.使える手法
5.予測したmask画像の0, 1化
6.クロップして予測してconcat
7.よかった手法、ダメだった手法



1.使ったネットワーク「HRNet」

使ったのは姿勢推定とかでSOTを出したネットワーク。すごい軽かったし、カスタマイズしやすかった。

f:id:trafalbad:20210905185837p:plain

reluをmishに変えたりしたのが効果的だった。

tensorflow版のmish活性化関数

import tensorflow_addons as tfa
x = tfa.activations.mish(x)

ちなみにクラスが1のセグメンテーションタスクだったので、アウトプットサイズも1でOK。


2.グレースケールのtifフォーマット画像の読み込み

グレースケールのtifフォーマットの画像は医療系データとか衛星データでよく見かける。

読み込みは普通のpngとかjpgとかとは違って工夫がいる。


pillowで読み込み

sar = Image.open('image.tif')

opencvでよみこみ

sar = cv2.imread('image.tif', -1)

tif用の画像の可視化関数。colablatelyで、使ってるものをjupyterで使えるようにした。

def cv2_imshow(a):
    """A replacement for cv2.imshow() for use in Jupyter notebooks.
    Args:
    a : np.ndarray. shape (N, M) or (N, M, 1) is an NxM grayscale image. shape
      (N, M, 3) is an NxM BGR color image. shape (N, M, 4) is an NxM BGRA color
      image.
    """
    a = a.clip(0, 255).astype('uint8')
    # cv2 stores colors as BGR; convert to RGB
    if a.ndim == 3:
        if a.shape[2] == 4:
            a = cv2.cvtColor(a, cv2.COLOR_BGRA2RGBA)
        else:
            a = cv2.cvtColor(a, cv2.COLOR_BGR2RGB)
    plt.imshow(a/255, 'gray'),plt.show()
    display.display(Image.fromarray(a))


f:id:trafalbad:20210905232204p:plain




3.tifフォーマット画像のrgb化

過去と現在の画像があるので
・差分をとる
・rgb化する方法

とかの方法がある。grayscaleで単に学習させるより、かなり効果的。

# load
im1 = load_tif('0VV.tif')
im2 = load_tif('0VH.tif')
im3 = load_tif('1VV.tif')

anno_test = cv2.imread('train.png', -1)

# RGB化
r = im1 * 255 
g = im2 * 255 
b = im3 * 255
rgb = np.dstack((r,g,b))
rgb = rgb(0, 255).astype('uint8')

f:id:trafalbad:20210905231932p:plain


4.使える手法

他の使える方法まとめ。

1.単純な引き算以外で差分をとる方法

diff = np.maximum(im1- im2], 0.5) - 0.5 
im_pred = np.heaviside(diff, 0)


2.とても細かい部分が多いので、細かい部分を除去する方法

def plot_correctness(im_truth, im_pred):
    r = im_truth
    g = im_pred 
    b = im_pred
    cv2_imshow(cv2.merge((r, g, b)))


def remove_blob(im_in, threshold_blob_area=25): 
    '''remove small blob from your image '''
    im_out = im_in.copy()
    contours, hierarchy = cv2.findContours(im_in.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    for i in range (1, len(contours)): 
        index_level = int(hierarchy[0][i][1]) 
        if index_level <= i:
            cnt = contours[i]
            area = cv2.contourArea(cnt)
            if area <= threshold_blob_area: 
                cv2.drawContours(im_out, [cnt], -1, 0, -1, 1)
    return im_out

im_out = remove_blob(im_pred * 255, threshold_blob_area=25)
plot_correctness(anno_test * 255, im_out.astype(np.uint8))


f:id:trafalbad:20210905232050p:plain



5.予測したmask画像の0, 1化

予測したgrayscaleの画像を0と1のマスクに変換する。
sigmoidを使うことがほとんどで、閾値で判別して、0か1を突っ込む。

閾値の設定が割と定まってないのが難儀。

def create_binary_mask(pred, threshold=0.1):
    mask = np.zeros_like(pred)
    mask[pred < threshold] = 0
    mask[pred > threshold] = 1
    return mask

# IOU計算
def calc_IoU(y, y_pred):
    by = np.array(y, dtype=bool)
    by_pred = np.array(y_pred, dtype=bool) 
    overlap = by * by_pred
    union = by + by_pred
    return overlap.sum() / float(union.sum())


6.クロップして予測してconcat

今回のコンペ は

・画像枚数が少ない(40枚以下)
・セグメンテーション領域が細かい
・画像サイズがでかい

ので、1枚の画像を複数にクロップして学習する。予測する画像もクロップしてから予測。そのあとくっつける(concat)。


こうすることで、細かい部分もかなり精密にセグメンテーションできるので、丸ごと画像を入れるより、かなり正確にセグメンテーションできる。(引用:qiita

f:id:trafalbad:20210905191827p:plain





7.よかった手法、ダメだった手法

効果があった手法

・差分とってrgb化
・augmentation :horizontal flip, vertical flip,
horizontal and vertical flip
SGD
・bce dice loss
・cropして予測した後concat(896にリサイズ後にサイズ448×448にクロップして4枚にした)
・unet, efficientunet
ヒストグラム平均化
・sigmoid
・0〜1で正規化

ダメだった手法

・adam
・focal loss, jacard loss
・小さい塊を除去する
・softmax
・標準化

終了2週間前に参加して、IOUが45%でブロンズに入れたので、よくできた方だと思う。手法は過去コンペとか読み漁ったのが良かった。

あとは試行回数とアイデア勝負。引き出しの大きさが重要だな感じた。

あとコンペ のdiscussionとかを読み込むことで、コンペ の概要を手っ取り早くしれるのであと出しの参加でも十分勝負できた。