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.py
にdef 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)
出力してみると真っ黒。
考えられる原因
- OpenCVはBGRだがこれはRGBを使っている
- 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''
最終的にはこんな感じに。
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)
出力結果
背景はデフォルトで黒、あとは人だけ取り出してみた。 上手いこととれた!
因みにこの人を横にすると人だと認識しない。
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の方が良さげではある。