ラズパイのAIカメラで物体(顔)検出に挑戦!【Python、Picamera、OpenCV、Keras】 | 9が好きな人のブログ
公開したプログラムの内容について、以下解説します。
GitHub公開プログラム
https://github.com/kotetsu99/ai_camera
↑のGitHub上で公開しているプログラムですが、主に4つのプログラムが存在します。
(1) 顔検出プログラム:01-face_detection.py
(2) 顔学習プログラム:02-cnn_face_train.py
(3) 顔認識プログラム:03-cnn_face_recognition.py
(4) AIカメラプログラム:04-ai_camera.py
このうち、(1)(3)については、以前にご紹介した画像認識AIプログラムのものとほぼ同じです。
(2)(4)については、新規のプログラムとなりますので、以下で解説を取り上げます。
顔学習プログラム
画像認識AIで紹介したプログラムと大体同じですが、多クラス分類ではなく、2クラス分類(本人か、他人か)に変更しているのが大きな特徴です。
まず、02-cnn_face_train.pyのmain関数の中にある学習モデル設定
# 2クラス分類を指定 cnn_model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
の箇所で、損失関数として2クラス分類向けの「binary_crossentropy」を選択しています。
学習および評価用の画像データ生成においても、2クラス分類向けのパラメータを設定しています。
def image_generator(classes): # トレーニング画像データ生成準備 datagen = ImageDataGenerator( rescale=1.0 / 255, zoom_range=0.2, horizontal_flip=False, validation_split=0.1) # ディレクトリ内の学習用画像を読み込み、データ作成 train_generator = datagen.flow_from_directory( train_data_dir, target_size=(img_width, img_height), color_mode='rgb', classes=classes, class_mode='binary', batch_size=batch_size, shuffle=True, subset = "training") # ディレクトリ内の評価用画像を読み込み、データ作成 validation_generator = datagen.flow_from_directory( train_data_dir, target_size=(img_width, img_height), color_mode='rgb', classes=classes, class_mode='binary', batch_size=batch_size, shuffle=True, subset = "validation") print(train_generator.class_indices) return (train_generator, validation_generator)
train_generator,validation_generator内の class_mode='binary' のパラメータ設定がそうですね。「本人フォルダ」「他人フォルダ」を「0」「1」という2値にそれぞれお割り当てて、表現します。
次にニューラルネットワークの定義です。
CIFAR-10 画像データセット向けのAIモデル(9層畳み込みネットワーク)の構成です。
def cnn_model_maker(): # 入力画像ベクトル定義(RGB画像認識のため、チャネル数=3) input_shape = (img_width, img_height, 3) # CNNモデル定義(Keras CIFAR-10: 9層ニューラルネットワーク) model = Sequential() model.add(Conv2D(32, (3, 3), padding='same', input_shape=input_shape)) model.add(Activation('relu')) model.add(Conv2D(32, (3, 3))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Conv2D(64, (3, 3), padding='same')) model.add(Activation('relu')) model.add(Conv2D(64, (3, 3))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(512)) model.add(Activation('relu')) model.add(Dropout(0.5)) model.add(Dense(1)) model.add(Activation('sigmoid')) return model
2クラス分類であるため、最後方にある出力ニューロンは1個で、活性化関数としてシグモイド関数を選択しています。シグモイド関数の出力は、本人か他人のどちらか一方である確率を返します。逆に言うと、1からそれをマイナスすれば他方である確率が得られます。
上記で挙げたKerasによるデータ生成、ディープラーニング実装は、以下の書籍が参考になります。
秀和システム (2019-06-05)
売り上げランキング: 169,082
売り上げランキング: 169,082
ディープラーニングに関する基本概念については、以下の書籍が参考になります。
SBクリエイティブ (2018-08-28)
売り上げランキング: 50,898
売り上げランキング: 50,898
AIカメラプログラム
AIカメラプログラム「04-ai_camera.py」について、重要箇所を取り上げます。本プログラムはpicameraライブラリを用いて、カメラ撮影を行い、映っている映像に対して、顔認識を行います。
まずは、カメラ初期化処理を以下で行っています。
with picamera.PiCamera() as camera: with picamera.array.PiRGBArray(camera) as stream: # カメラの解像度を320x320にセット camera.resolution = (320, 320) # カメラのフレームレートを15fpsにセット camera.framerate = 15 # ホワイトバランスをfluorescent(蛍光灯)モードにセット camera.awb_mode = 'fluorescent'
コメントに書いてある通りのことをやっていますが、picameraを使用する際には、大体こうやるのがお約束だそうです。最初の2つのwith文で、カメラを扱うためのインスタンスを生成し、さらに映像をテンソル表現したRGBデータをstreamという変数として定義しています。with文を使用しているということで、これらは暗黙の開始処理、終了処理が必要らしく、それをwith文を使って省略記述しています。
次にループでカメラ映像を回しながら、映っている画像に対し、顔認識を行っていきます。
# 時間計測開始 start_time = time.time() process_time = 0 # 制限時間まで顔認識実行 while process_time < time_limit : # 本人認識フラグ person_flg = False # stream.arrayにBGRの順で映像データを格納 camera.capture(stream, 'bgr', use_video_port=True) # 顔認識 image, person_flg = detect_face(stream.array, model, person_flg) # カメラ映像をウインドウに表示 cv2.imshow('frame', image) # 'q'を入力でアプリケーション終了 key = cv2.waitKey(1) if key & 0xFF == ord('q'): break # 本人が顔検出された場合に何か処理を実施 if person_flg == True : # 何か処理を実施(ここから) print('本人です') # 何か処理を実施(ここまで) # streamをリセット stream.seek(0) stream.truncate() # 経過時間(分)計算 process_time = (time.time() - start_time) / 60 #print('process_time = ', process_time, '[min]') # 経過時間(分)計算 process_time = (time.time() - start_time) / 60 #print('process_time = ', process_time, '[min]') cv2.destroyAllWindows()
picameraでカメラ映像を回すときは、
・capture メソッドで映像を取得する
・stream.seek(0),stream.truncate() で映像を破棄する
の2つをwhileループで回し続ける処理で実装します。各ループの間に、取得したカメラ映像データ変数(stream)に対して、顔認識を実施、ウインドウに表示するという要領です。
ラズパイによるカメラ認識については、以下の書籍が参考になります。
講談社 (2018-03-14)
売り上げランキング: 72,812
売り上げランキング: 72,812
顔認識を行う、detect_face関数については、以前に紹介した顔認識プログラムに登場したものとほぼ同じです。
ループには制限時間を設けてあり、time_limit(設定では30分)を超過するとループを抜け、プログラムが終了します。ウインドウを選択して「q」を押下してもループ抜け→ウィンドウを閉じる→終了という流れです。
また、以下の箇所ですが、
# 本人が顔検出された場合に何か処理を実施 if person_flg == True : # 何か処理を実施(ここから) print('本人です') # 何か処理を実施(ここまで)
本人が映った場合に、何か処理をさせたい(音を鳴らしたい、何かの別プログラムを走らせたい)場合に設けているコード箇所です。本人検出をトリガーとして、ラズパイに何かをさせたい場合に、ここに処理を記述してカスタマイズできれば面白いことができるかなと。