Raspberry PiでOpenCVを使って顔を認識する
この記事の内容はRaspberry Pi 4と3の両方で動作確認済みです
ブログ管理者のP.Hです!
今回は、標準のカメラモジュールを使って顔認識をしてみたいと思います。OpenCVを使うと思ったよりかなり簡単にできました。いろいろと使い道はあると思いますし、単純におもしろかったです。
それでは、顔認識する手順を紹介していきます。
OpenCVのインストール
OpenCVというモジュールを使って顔認識を行います。下記コマンドを実行して、インストールをしてください。
$ sudo apt-get install libhdf5-dev libhdf5-serial-dev libhdf5-103 $ sudo apt-get install libqtgui4 libqtwebkit4 libqt4-test python3-pyqt5 $ sudo apt-get install libatlas-base-dev $ sudo apt-get install libjasper-dev $ sudo pip3 install opencv-python
これだけだとエラーが出てしまいます。下記のコマンドで.bashrcを開いて、環境変数の設定をします。
nano ~/.bashrc
# 下記の1行を追加する export LD_PRELOAD=/usr/lib/arm-linux-gnueabihf/libatomic.so.1
その後、再起動するとエラーが発生しなくなります。
学習済みモデル(カスケード分類器)のダウンロード
本来であれば、多くのデータから機械学習をして顔認証するためのモデルを作る必要があるのですが、OpenCVでは学習済みモデルをダウンロードできます。ホームディレクトリに./face/model
というフォルダを作成して、学習済みモデルのファイルをダウンロードします。ダウンロード後、ファイルを解凍します。
$ mkdir -p ~/face/model $ cd ~/face/model $ wget https://github.com/opencv/opencv/archive/master.zip $ unzip master.zip
学習済みモデルは下記のパスにあります。
~/face/model/opencv-master/data/haarcascades
pythonコードでnumpy、pillowを使用しますので、あらかじめインストールしておきます。
sudo pip3 install numpy pillow
顔認証するpythonコード
準備ができましたので、実際に顔認識をしてみたいと思います。
下記のpythonコードを実行してください。Raspberry Piのカメラに顔が映っていると四角い枠が表示されます。コードの内容はコード内のコメントを確認してください。
import numpy as np import cv2 # 学習済みモデルを読み込む faceCascade = cv2.CascadeClassifier('/home/pi/face/model/opencv-master/data/haarcascades/haarcascade_frontalface_default.xml') # faceCascade = cv2.CascadeClassifier('/home/pi/face/model/opencv-master/data/haarcascades/haarcascade_eye.xml') # カメラで動画を撮影する カメラ1台の場合は引数に0 or -1を設定する cap = cv2.VideoCapture(0) cap.set(3,640) # 横幅を設定 cap.set(4,480) # 縦幅を設定 while True: # フレーム毎にキャプチャする ret, img = cap.read() # 顔検出の負荷軽減のために、キャプチャした画像をモノクロにする gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 顔検出のパラメータの設定 faces = faceCascade.detectMultiScale( gray, scaleFactor=1.2, minNeighbors=5, minSize=(20, 20) ) # 顔検出時に四角い枠を表示 for (x,y,w,h) in faces: cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2) roi_gray = gray[y:y+h, x:x+w] roi_color = img[y:y+h, x:x+w] # imshow関数で結果を表示する cv2.imshow('video',img) # ESCが押されたら終了する k = cv2.waitKey(30) & 0xff if k == 27: break cap.release() cv2.destroyAllWindows()
顔が映ると四角い枠が表示されました。顔を認識できています。OpenCVを使うと、こんなに簡単に人の顔を認識することができました。
学習済みモデルを変えると、目を認識したりすることもできます。
- 目を認識する ⇒ haarcascade_eye.xml
学習モデルを自分で作成する
上記では既存の学習モデルを使用しました。ここからは、自分で画像データを作り、学習して学習モデルを作成する方法を紹介します。どのくらい一致しているかパーセントで表示したり、映っている人の顔から名前を表示したりすることができます。
画像データを集める
顔の写真を30枚撮り、これを学習用の画像データとします。画像データを保存するフォルダを作成します。
$ mkdir ~/face/dataset
以下のような流れになります。顔認識をしたい人全員の写真を撮ります。
- 下記のpythonコードを実行します
- ユーザーIDを入力します(1と入力)
- 30枚写真を撮ります。dataset/User1*という名前で保存されます。
- 別の人に変わって、pythonコードを再度実行します。
- ユーザーIDを入力します(2と入力)
- 30枚写真を撮ります。dataset/User2*という名前で保存されます。
- 別の人に変わって、pythonコードを再度実行します。 あとはユーザーIDを1ずつ増やしていき、全員分の写真をとります。
下記コードを実行して、30枚写真を撮り、画像データを収集します。
import cv2 import os cam = cv2.VideoCapture(0) cam.set(3, 640) cam.set(4, 480) face_detector = cv2.CascadeClassifier('/home/pi/face/model/opencv-master/data/haarcascades/haarcascade_frontalface_default.xml') # ユーザーIDの入力 face_id = input('ユーザーID(1,2,3...)を入力して、Enterキーを押してください。') print("画像データを収集しています。カメラを見てしばらくお待ちください。") # 画像データ(写真)を何枚撮ったか数えるカウンター変数 count = 0 while(True): ret, img = cam.read() gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_detector.detectMultiScale(gray, 1.3, 5) for (x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h), (255,0,0), 2) count += 1 # dataset/フォルダにUser*と名前を付けて写真を保存 cv2.imwrite("/home/pi/face/dataset/User." + str(face_id) + '.' + str(count) + ".jpg", gray[y:y+h,x:x+w]) cv2.imshow('image', img) # ESCキーを押すか、30枚写真を撮ったら終了 k = cv2.waitKey(100) & 0xff if k == 27: print("画像データの収集を中断しました。") break elif count >= 30: print("画像データの収集が完了しました。") break cam.release() cv2.destroyAllWindows()
これで画像データが集まりました。
学習して学習モデルを作成する
集めた画像データを使って学習をします。学習モデルを保存するフォルダを作成します。
$ mkdir ~/face/trainer
下記コードを実行して、学習を行います。
※私の環境では、下記のエラーが発生しました。
recognizer = cv2.face.LBPHFaceRecognizer_create() AttributeError: module 'cv2.cv2' has no attribute 'face'
下記コマンドでopencv-contrib-pythonモジュールをインストールすることで改善しました。
$ sudo pip3 install opencv-contrib-python
注意事項
- opencv-pythonとopencv-contrib-pythonの両方をインストールするとうまく動作しないとWebに情報がありました。その場合は、opencv-pythonをアンインストールして改善されるか試してみてください。
import cv2 import numpy as np from PIL import Image import os # 画像データのパスを指定 path = '/home/pi/face/dataset' # 顔認識のためにOpenCVモジュールのLBPH(LOCAL BINARY PATTERNS HISTOGRAMS)のインスタンスを生成 recognizer = cv2.face.LBPHFaceRecognizer_create() detector = cv2.CascadeClassifier("/home/pi/face/model/opencv-master/data/haarcascades/haarcascade_frontalface_default.xml") # 画像データとラベル(id)を取得する関数 def getImagesAndLabels(path): imagePaths = [os.path.join(path,f) for f in os.listdir(path)] faceSamples=[] # 画像データを格納するリスト ids = [] # idを格納するリスト # datasetフォルダ内にある全フォルダをループで回す # 画像をグレースケール化してfaceSamplesリストに追加、idはidsリストに追加 for imagePath in imagePaths: PIL_img = Image.open(imagePath).convert('L') # グレースケールに変換 img_numpy = np.array(PIL_img,'uint8') id = int(os.path.split(imagePath)[-1].split(".")[1]) faces = detector.detectMultiScale(img_numpy) for (x,y,w,h) in faces: faceSamples.append(img_numpy[y:y+h,x:x+w]) ids.append(id) return faceSamples,ids print ("学習中です。しばらくお待ちください。") # 上記関数を使用して、グレースケール化した画像とユーザーIDを取得 faces,ids = getImagesAndLabels(path) # 学習する recognizer.train(faces, np.array(ids)) # 学習済みモデルをtrainer/trainer.ymlファイルに出力する recognizer.write('/home/pi/face/trainer/trainer.yml') print("{0}種類の顔を学習しました。プログラムを終了します。".format(len(np.unique(ids))))
これで自分で撮った画像データを使用して学習をし、学習モデルを作ることができました。
自分で作成した学習モデルを使用して顔認識をする
基本的には一番はじめの顔認識と同じです。自分で作った学習済みモデルを読み込み、どのくらい一致しているかをパーセント表示するコードが追加されています。
import cv2 import numpy as np import os # 自分で作成した学習モデルを読み込む recognizer = cv2.face.LBPHFaceRecognizer_create() recognizer.read('/home/pi/face/trainer/trainer.yml') cascadePath = "/home/pi/face/model/opencv-master/data/haarcascades/haarcascade_frontalface_default.xml" faceCascade = cv2.CascadeClassifier(cascadePath) font = cv2.FONT_HERSHEY_SIMPLEX id = 0 # ユーザーIDを名前に置き換えるためのリストを作る # 例 id=1(リストの要素1) ⇒ pi、id=2 ⇒ raspberry names = ['None', 'pi', 'raspberry', 'test1', 'test1', 'test3'] cam = cv2.VideoCapture(0) cam.set(3, 640) cam.set(4, 480) # 顔として認識する最小サイズを定義する minW = 0.1 * cam.get(3) minH = 0.1 * cam.get(4) while True: ret, img =cam.read() gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) faces = faceCascade.detectMultiScale( gray, scaleFactor = 1.2, minNeighbors = 5, minSize = (int(minW), int(minH)), ) for(x,y,w,h) in faces: cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2) # 顔認識の信頼度を取得 100~0 0の時が100%一致 id, confidence = recognizer.predict(gray[y:y+h,x:x+w]) if (confidence < 100): # 顔認識しているidから名前に変換 id = names[id] confidence = " {0}%".format(round(100 - confidence)) else: id = "unknown" confidence = " {0}%".format(round(100 - confidence)) # 名前を表示 cv2.putText(img, str(id), (x+5,y-5), font, 1, (255,255,255), 2) # 信頼度(%)を表示 cv2.putText(img, str(confidence), (x+5,y+h-5), font, 1, (255,255,0), 1) cv2.imshow('camera',img) k = cv2.waitKey(10) & 0xff if k == 27: break print("プログラムを終了します。") cam.release() cv2.destroyAllWindows()
これでカメラの映像に顔が映ったときに、四角い枠と"pi"と"75%"のように表示されれば、成功です。顔を認識して、好きなプログラムを実行させることができそうです。OpenCV、すごいですね。作っていてとてもおもしろかったです。是非、試してみてください。
本記事は、下記のHPを参考にさせていただいています。本当に感謝です。