- 2016.10.04
- 技術ブログ
感情がわかる!?ラズパイとEmotion APIで表情認識をしてみよう
秋も深まり、札幌は日に日に涼しくなってきています。
涼しい今のうちに、色々と遊びに行きたいtacckです。こんにちは。
今回は、Raspberry Piでカメラの利用と画像処理として、顔の認識を行なってみます。
ただ、ラズパイでカメラを利用した顔認識の利用例は、ネット上にすでにたくさんあるので、今回はそこにもう一手間加えてみたいと思います。
最近は、いろいろな所で「人の感情」に関する研究が行なわれており、その成果をWebAPIとして利用できたりします。
その一つとしてMicrosoftが提供している"Cognitive Services Emotion API"というものを利用し、「表情の認識」を行なってみたいと思います。
前提
今回利用した機材はこちらです。
- Raspberry Pi 2 Model B
- Raspberry Pi Camera Module
OSは"Raspbian JESSIE"の最新状態になっている前提とします。
また、イーサネット、Wi-Fiなどによって、インターネットに接続できる状態となっている前提とします。
パッケージのインストールなど
表情認識を行なうまでに、処理としては下記のような流れとなります。
- カメラで撮影
- 撮影した画像に「顔」が含まれているか分析
- 「顔」が含まれていればEmotion APIへ画像を送信
- Emotion APIの分析結果を画像に描画して保存
このうち、撮影した画像に「顔」が含まれているか分析
とEmotion APIの分析結果を画像に描画して保存
には"
OpenCV"という「画像処理・画像解析および機械学習等の機能」を持ったライブラリを利用します。
ここでは詳細は割愛しますが、非常に手軽で強力なライブラリとなっています。
また、カメラで撮影
について、今回利用するラズパイ標準カメラをOpenCVで使う方法を検索すると、かなり手間のかかる手順の紹介ページが出てきます。
しかし、現在では"Picamera"というライブラリを使うことで、標準カメラを簡単に取り扱え、撮影した画像をOpenCVで容易に処理できるようになります。
では、順番にセットアップしていきましょう。
Picamera
公式にインストール手順があるので、その通りに実行すれば良いです。
1 2 |
$ sudo apt-get update $ sudo apt-get install python-picamera |
OpenCV
こちらも、パッケージが準備されているので、下記コマンドを実行すればすぐに使えるようになります。
1 2 |
$ sudo apt-get update $ sudo apt-get install libopencv-dev python-opencv |
Emotion API
こちらは、先に紹介したとおり、MicrosoftのCognitive Servicesの一つです。
Emotion APIでは、画像の中から人の顔を検出し、その表情について複数の感情の評価を行なって、結果を返却してくれるものです。
感情については、下記が評価されます。
- anger (怒り)
- contempt (軽蔑)
- disgust (嫌悪)
- fear (恐怖)
- happiness (喜び)
- sadness (悲しみ)
- surprise (驚き)
- neutral (はっきりしない)
利用方法ですが、ユーザ登録を行なえば、ある程度は無料で使えるようになっています。
Microsoft アカウントを利用してサインインを行なってください。
サインインに成功すると、上記のように利用するAPIの選択画面に遷移します。
今回は、Emotion API
を選択しましょう。
ページ下部に規約同意のチェックボックスがあるのでチェックを入れ、Subscribe
ボタンをクリックしてください。
成功したら、上記のようにAPIの登録状況を確認できるページへ遷移します。
今回は、このページのKey 1
を利用します。標準では非表示の状態ですが、Show
をクリックすればページ上に表示されます。
実装
今回利用したコードは下記です。
特にEmotion APIを呼び出して結果を画像に描画する処理については、こちらのページを参考にさせていただきました。
上記セットアップ時に表示されたKey 1
の値を、下記コードの[Key 1]
に置き換えて利用してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
import io import time import picamera import cv2 import numpy as np import httplib import json # Azure API Key api_key = [Key 1] # CV2 cascade_path = "/usr/share/opencv/haarcascades/haarcascade_frontalface_alt.xml" # Image Size camera_width = 640 camera_height = 480 ##### def getEmotion(image, headers): try: conn = httplib.HTTPSConnection('api.projectoxford.ai') conn.request("POST", "/emotion/v1.0/recognize?", image, headers) response = conn.getresponse() data = response.read() conn.close() return data except Exception as e: print("[Errno {0}] {1}".format(e.errno, e.strerror)) print(e.message) def drawEmotion(image, data): font = cv2.FONT_HERSHEY_PLAIN font_size = 1 data = json.loads(data) for face in data: f_rec = face['faceRectangle'] width = f_rec['width'] height = f_rec['height'] left = f_rec['left'] top = f_rec['top'] f_rec = face['scores'] f_rec = sorted(f_rec.items(), key=lambda x:x[1],reverse = True) cv2.rectangle(image, (left, top), (left + width, top + height), (130, 130, 130), 3) cv2.rectangle(image, (left + width, top), (left + width + 150, top + 50), (130, 130, 130), -1) for i in range(0, 5): val = round(f_rec[i][1], 3) emo = f_rec[i][0] cv2.rectangle(image, (left + width, top + 10 * i), (left + width + int(val * 150), top + 10 * (i + 1)), (180, 180, 180), -1) cv2.putText(image, emo + " " + str(val), (left + width, top + 10 * (i + 1)), font, font_size, (255,255,255), 1) ##### # Create the in-memory stream stream = io.BytesIO() with picamera.PiCamera() as camera: camera.resolution = (camera_width, camera_height) camera.start_preview() time.sleep(0.5) camera.capture(stream, format='jpeg') # Construct a numpy array from the stream data = np.fromstring(stream.getvalue(), dtype=np.uint8) # "Decode" the image from the array, preserving colour image = cv2.imdecode(data, 1) # Detect face image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) cascade = cv2.CascadeClassifier(cascade_path) facerect = cascade.detectMultiScale(image_gray, scaleFactor=1.1, minNeighbors=1, minSize=(1, 1)) print "face rectangle" print facerect if len(facerect) > 0: save_file_name = "face_detect.jpg" save_emotion_file_name = "face_emotion.jpg" cv2.imwrite(save_file_name, image) headers = { 'Content-Type': 'application/octet-stream', 'Ocp-Apim-Subscription-Key': api_key, } image_load = open(save_file_name, 'rb') data = getEmotion(image_load, headers) print data drawEmotion(image, data) cv2.imwrite(save_emotion_file_name, image) |
実行した結果、上記のように顔が検知されて、横に表情の分析結果が表示されています。
まとめ
ラズパイと標準カメラを利用して、人の顔の表情認識を行なうことができました。
これを応用すれば、例えば「笑顔の写真」だけを保存する・・・、というようなこともできますね。
みなさんも、ラズパイのカメラで色々と遊んでみてください!