LoginSignup
0
0

More than 1 year has passed since last update.

pillow+numpyで入力した画像からdlibで顔検知しようとしてfor文2枚目で止まった

Last updated at Posted at 2022-08-16

はじめに

よくあるpythonのdlibを使用して顔を検出するやつ、だいたいがopencv2を使用してcv2.imreadとかして読み込んでいるけど、opencv2を使いたくなかった。
そこでpillowで読み込んだ画像をnumpy形式に変換してdlibに食わせようとした。

で、画像が1枚の時は無事に動作、画像をfor文で複数与えようとすると2枚目で顔検出しなくなった。

どういう状況?

コードを一部
入力する画像は同じ人物を同じ角度から撮った顔の画像。

import dlib
from PIL import Image 
import numpy as np

face_detector = dlib.get_frontal_face_detector()
image_list=["01.jpg","02.jpg","03.jpg","04.jpg","05.jpg"]
for img_path in image_list:
    img = np.array(Image.open(img_path),dtype=np.uint8) ##画像を読み込む
    img = img[:, :, [2, 1, 0]] ##opencvとおなじBGR順にする

    faces = face_detector(img, 1)
    #(後処理)

このとき、01.jpgはちゃんと顔検出する。02.jpgが顔検出しない。
同じ画像を5枚並べてもやっぱり2枚目で検出しない。
順番を入れ替えても1枚目成功、2枚目失敗となる。

IndexError: index 0 is out of bounds for axis 0 with size 0

エラーとしてはfacesで取得できるはずの顔検出の領域が入っていないと怒られている。つまり顔検出できていない。

解決した方法

①opencvを使用する

    img = cv2.imread(img_path)

画像の読み込みをopencvにすれば問題ない。でもこれは根本的な解決になっていない。

②変数を新規宣言する

    img = np.array(Image.open(img_path),dtype=np.uint8)
    img_p = img[:, :, [2, 1, 0]]

読み込んだあとに、BGR順に変更するがこの時に同じ変数を用いず、新規変数にする。
こうすることで解決した。

追記
ちゃんとDeepCopyしたほうが安定する。

なぜこんなことが起こったのか?

おそらくnumpy内部でのポインタ管理が云々かんぬんだと思われる。
自身を参照するとなると見た目上は変わっていてもdlibに入れる際にちゃんとした参照になっていないんじゃないかと。
このあたりはちゃんと調査していない。
ダメだった時におけるimg配列をdatashapestridesとかを使ってポインタを追えばわかるかもしれない。

余談

>> img_p = np.array(Image.open(img_path),dtype=np.uint8) ##PILで読み込む
>> img_p = img_p[:, :, [2, 1, 0]]
>> img_c = cv2.imread(img_path) ##OpenCVで読み込む
>> print(np.allclose(img_p,img_c)) ##配列の一致を確認する
   True

numpy的には新規変数を宣言しない場合でも、データは一致という。なおこの状態で動かすと5枚とも顔検出する。np.allcloseを挟むことによってポインタ参照が再整理されたのか?
また途中でほかのnumpyの処理を挟むと同じように再整理されたりされなかったりする。

0
0
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
0
0