keras-deeplab-v3-plusで人だけとってみる

github.com

Semantic Segmentationで人をとってきたいのでこのアーキテクチャを使って人と背景を分ける。

準備

# 仮想環境の準備
$ conda create -n keras-deeplab-v3-plus
$ source activate keras-deeplab-v3-plus

# モジュールインストール
$ conda install tqdm
$ conda install numpy
$ conda install keras

# 重みダウンロード
$ python extract_weights.py 
$ python load_weights.py 

Jupyterを使って画像を出力

ここからは自分用にカスタマイズしていく。 多クラス分類だが、人だけとれれば良い。

デフォルトで入っているimage1.jpgを使う。

import model
import cv2

img = cv2.imread('./imgs/image1.jpg')
img = cv2.resize(img, (512, 512)) # imgは0〜255の値をとる

model_dlv3 = model.Deeplabv3()

predicted = model_dlv3.predict(img[np.newaxis, ...])
print(predicted.shape) 
# (1, 512, 512, 21)

21クラス分類だが、どれが人なのか。
閾値がどうなっているのか知りたい。

が、プログラム内に無さげ。
「semantic segmentation 21 class names」でぐぐる

一番上にぽいのが出てくる。 github.com 0が背景で、15が人と仮定する。

追記
というかmodels.pydef Deeplabv3(weights='pascal_voc', ...という行もあるのでこれで合っていると思われる。

person_score = predicted[0, :, : ,15]
back_score = predicted[0, :, :, 0]

mask = (person_score > back_score).astype("uint8") * 255
cv2.imwrite("test.jpg", mask)

出力してみると真っ黒。

考えられる原因

  1. OpenCVはBGRだがこれはRGBを使っている
  2. 0〜255ではなく-1〜1などで処理している

追記
入力画像の大きさが小さすぎた&人が横になっていたのが原因でした(横になっていた際の出力例、RGBに変換した画像は下の方にあります)

入力の際の前処理

ところで、これはモデルとしてMobileNetv2を使用していた。
画像の前処理はどうやってるんだろう?
Kerasがmobilenetv2を提供していて、ResNetなどもpreprocess_input的な関数は提供している(らしい)。
ということで調べてみる。

こういう当たりが付けられるの強い。
といってもここらへん全部友人がやってくれた。圧倒的人任せ。

「mobilenetv2 preprocess keras」でぐぐると以下のサイトが見つかる。 github.com

preprocess_inputというのがあるのでそれを使ってみよう。

import cv2
import numpy as np

import model

from keras.applications.mobilenetv2 import preprocess_input

img = preprocess_input(cv2.imread('./imgs/image1.jpg')) ## ここでエラー
img = cv2.resize(img, (512, 512))

エラー発生

TypeError: ufunc 'true_divide' output (typecode 'd') could not be coerced to provided output parameter (typecode 'B') according to the casting rule ''same_kind''

参考: TypeError: ufunc 'true_divide' output (typecode 'd') could not be coerced to provided output parameter (typecode 'B') according to the casting rule ''same_kind'' #8635

最終的にはこんな感じに。

import cv2
import numpy as np

import model

from keras.applications.mobilenetv2 import preprocess_input

img = preprocess_input(cv2.imread('./imgs/test.jpg').astype("float"))
img = cv2.resize(img, (512, 512))

img.max() # 0.9921875
model_dlv3 = model.Deeplabv3()

predicted = model_dlv3.predict(img[np.newaxis, ...])

person_score = predicted[0, :, : ,15]
back_score = predicted[0, :, :, 0]

mask = (person_score > back_score).astype("uint8") * 255
cv2.imwrite("test.jpg", mask)

出力結果

背景はデフォルトで黒、あとは人だけ取り出してみた。 上手いこととれた! f:id:saneeeatsu:20180820183446p:plain

因みにこの人を横にすると人だと認識しない。 f:id:saneeeatsu:20180820183428p:plain

BGR→RGBにしてみる

OpenCVではデフォルトでBGRで扱うが、もしこのアーキテクチャではRGBで扱っていて、更に人を肌の色などで認識している場合、精度が異なってくる。
ってことで試す。

と言っても1行追加するだけだけど。

img = preprocess_input(cv2.imread('./imgs/R0_L0_ono_001360.jpg').astype("float"))
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 追加行
img = cv2.resize(img, (512, 512))

元画像を載せていないのでよくわからないと思うが、本来白色になってほしいところ(スカートの白色の部分)が白色にきちんとなっているしRGBの方が良さげではある。 f:id:saneeeatsu:20180820184540p:plain