Edited at

keras-deeplab-v3-plusで人だけとってみる(ソース有り)

この投稿は自分のブログの転載です。

keras-deeplab-v3-plusで人だけとってみる - 機械音痴な情報系

Semantic Segmentationで人をとってきたいのでkeras-deeplab-v3-plusを使ってみました。

勿論本来は人以外も色々なものをとってこれます。

keras-deeplab-v3-plus - Github

Screen Shot 2018-09-07 at 14.37.33.png


準備

# 仮想環境の準備

$ 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を使って画像を出力

ここからは自分用にカスタマイズしていく。

多クラス分類だが、人だけとれれば良い。

今回は以下の画像を使用します(自分)。

frame.jpg

import model

import cv2

img = cv2.imread('./test.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」でぐぐる。

一番上にぽいのが出てくる。

pascal-voc-classes.txt(NVIDIA/DIGITS) - Gtihub

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」でぐぐると以下のサイトが見つかる。

mobilenet_v2.py(keras-team/keras) - Github

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

import cv2

import numpy as np

import model

from keras.applications.mobilenetv2 import preprocess_input

img = preprocess_input(cv2.imread('./test.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('./test.jpg').astype("float"))

img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (512, 512))

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("./person_musk.jpg", mask)


出力結果

背景はデフォルトで黒、あとは人だけ取り出してみた。

上手いこととれた!

これで画像を重ねて背景変えるなり色々出来そう。

person_musk.jpg

他の画像では以下のようになりました。

20180820183446.png

因みにこの人を横にすると人だと認識しない。

20180820183428.png


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の方が良さげではある。

20180820184540.png