LoginSignup
7
11

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-09-07

この投稿は自分のブログの転載です。
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

7
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
11