PepperのAPIを使って表情認識

[PepperのAPIを使って年齢認識](http://qiita.com/Atelier-Akihabara/items/4162192129f366da1240#alfacecharacteristics-api)で紹介した**ALFaceCharacteristics API**を用いることで、笑顔などの表情に関する認識をおこなうことができます。ここでは、APIが提供する笑顔検出と、その他の表情認識の方法の2通りを試してみることにします。

なお、 **ALFaceCharacteristics APIはバーチャルロボットでは動作確認手段はなく、Pepper実機が必要になります。** アルデバラン・アトリエ秋葉原などでPepper実機を使って実験などおこなってみていただければと思います。 (予約URL：http://pepper.doorkeeper.jp/events)

#プロジェクトファイルの入手方法

このチュートリアルに関するプロジェクトファイルはGitHub https://github.com/Atelier-Akihabara/pepper-face-characteristics-example にて公開しています。
GitHubにあるコードの取得方法にはいくつかありますが、[Download ZIP]リンクからアーカイブを取得するのが簡単な方法のひとつです。他にもgit関連ツールを利用する方法などさまざまな方法がありますので、状況に応じて調べてみてください。


#笑顔の判定

`ALFaceCharacteristics` APIを利用することで、認識した顔に関して、目の細さや口角が上がっているかなどの画像的な特徴から、笑顔度を判定することができます。この値を用いて、Pepperが認識している人間が笑顔かどうかを判定することが可能です。

##メモリイベント FaceCharacteristics/PersonSmiling の利用

笑顔判定をもっとも簡単におこなう方法として、 `FaceCharacteristics/PersonSmiling` イベントを利用する方法があります。
まず、ドキュメントの内容を確認してみましょう。

###ドキュメントの確認

このイベントに関する詳細は、Choregrapheドキュメントの NAOqi Developer guide > NAOqi Framework > NAOqi API > NAOqi PeoplePerception > ALFaceCharacteristics > ALFaceCharacteristics API にて説明されています。 `FaceCharacteristics/PersonSmiling` のリンクをクリックすると、以下のような説明が書いてあります。

> Event: "FaceCharacteristics/PersonSmiling"
> callback(std::string *eventName*, int *id*, std::string *subscriberIdentifier*)
>
> Raised when a person has a smile degree above the current threshold (default = 0.7).
> Parameters:	
>
> * *eventName* (std::string) – “FaceCharacteristics/PersonSmiling”
> * *id* – ID of the person as defined by the ALPeoplePerception API.
> * *subscriberIdentifier* (std::string) –

笑顔度(smile degree, これは0.0～1.0で表現されます)が、しきい値(デフォルトは0.7)を越える人がいるとこのイベントが発生するといったことが説明されています。

また、このイベントによって渡される値は `id` です。これは、[ALPeoplePerception: 人の一覧の取得](http://qiita.com/Atelier-Akihabara/items/4162192129f366da1240#alpeopleperception-%E4%BA%BA%E3%81%AE%E4%B8%80%E8%A6%A7%E3%81%AE%E5%8F%96%E5%BE%97)ですでにみてきた、人の識別子です。
つまり、この `FaceCharacteristics/PersonSmiling` で得た値に基づいて、 `PeoplePerception/Person/<ID>/AgeProperties` などを取得することで、笑顔であると検出した人に関する追加情報を得ることが可能ということがわかります。

###メモリウォッチャーでのメモリイベントの動作確認

アプリケーションを作成する前に、[メモリウォッチャー](http://qiita.com/Atelier-Akihabara/items/0a5826fb018a4d0e4268#3-4)でイベントの動作を確認してみましょう。
まず、メモリウォッチャーに確認したいイベントの内容を設定します。

1. ChoregrapheをPepper実機に接続します
1. [メモリウォッチャー]パネルから **<監視するメモリキーの選択>をダブルクリック**します
    ![memory-watcher.png](https://qiita-image-store.s3.amazonaws.com/0/54394/4ec1403d-5a8d-ce53-3d40-97ef1ce838f9.png)
    [メモリウォッチャー]パネルが表示されない場合は、[表示]メニューの[メモリウォッチャー]がチェックされているかどうかを確認してください。

2. まずは `FaceCharacteristics/PersonSmiling` を確認したいので、**[フィルター]に PersonSmiling と入力 [A]** し、`FaceCharacteristics`の配下の**[PersonSmiling]をチェック [B]**し、[OK]ボタンをクリックします

    ![add-person-smiling.png](https://qiita-image-store.s3.amazonaws.com/0/54394/adf8740d-95de-74a6-e8cb-63f5eb67ae66.png)

    すると、メモリウォッチャーに `FaceCharacteristics/PersonSmiling` が追加されます。

    ![memory-watcher-2.png](https://qiita-image-store.s3.amazonaws.com/0/54394/9c76f6c8-0a07-60f4-243e-400941e3617e.png)

3. 次に、人のIDの値を確認するため、 `PeoplePerception/VisiblePeopleList` も監視します。3.と同様に、**[フィルター]に VisiblePeopleList と入力 [A]** し、`PeoplePerception`配下の**[VisiblePeopleList]をチェック [B]**し、[OK]ボタンをクリックします

    ![add-visible-people-list.png](https://qiita-image-store.s3.amazonaws.com/0/54394/d879c450-b4c3-6a58-bd75-488c3fe9c5c0.png)

    以下のように、[メモリウォッチャー]パネルに `FaceCharacteristics/PersonSmiling` と `PeoplePerception/VisiblePeopleList` が追加されていることを確認してください。

    ![memory-watcher-3.png](https://qiita-image-store.s3.amazonaws.com/0/54394/60017d04-c09f-90ab-9b92-aa9589281c3b.png)

これで、メモリイベントの動作がどのようになっているのか、動作確認をおこなってみましょう。
Pepperの前に立つと、メモリウォッチャーの内容に以下のような変化があらわれるはずです。

![memory-watcher-visible.png](https://qiita-image-store.s3.amazonaws.com/0/54394/50f6fcf0-57d3-8a2e-0648-d8805ddcc7c2.png)

`PeoplePerception/VisiblePeopleList` に `[6602]` という数値が表示されました。これは、Pepperがカメラで認識可能な領域に人を一人認識し、その人には`6602`という識別子が設定されていることを示します。(この値は、その時点でのPepperの内部状態により異なります。)
次に、Pepperの前で笑ってみます。口角を上げたり、目を細めたり、歯を見せたりして笑顔を作ってみてください。

![memory-watcher-smiling.png](https://qiita-image-store.s3.amazonaws.com/0/54394/e02dc68e-c724-de67-56f2-3635746d4db5.png)

すると、`FaceCharacteristics/PersonSmiling`に`6602`と表示されました。これは、Pepperが人(識別子は`6602`)の笑顔度が0.7より大きくなったことによってこのイベントが発行され、その人の識別子が `FaceCharacteristics/PersonSmiling` に出力されたことを示しています。

###アプリケーションをつくってみる

`FaceCharacteristics/PersonSmiling`の動きが確認できたところで、このイベントを使ってアプリケーションを作成してみましょう。
ここではサンプルとして、**起動時に「僕に向かって笑ってもらえますか？」としゃべり、笑顔を認識すると、「(ID)さん、いい笑顔です！」としゃべるアプリケーション**を作成してみます。

なお、サンプルプロジェクトは **subscribe-person-smiling** です。GitHubから取得したファイルのうち、 `subscribe-person-smiling` フォルダ内の `subscribe-person-smiling.pml` をダブルクリックするなどして開くことができます。
以下のような手順で作成しています。

1. フローダイアグラム左側の**メモリイベント追加[+]ボタンをクリック**し、フィルターに**PersonSmilingと入力[A]**し、**FaceCharacteristics/PersonSmilingをチェック[B]** し[OK]ボタンを押します
    ![subscribe-person-smiling-event.png](https://qiita-image-store.s3.amazonaws.com/0/54394/b8946359-6e1d-22ec-a6cb-d39b05e09817.png)

2. 以下のボックスをフローダイアグラムに配置します
    * standardボックスライブラリ
        * Audio > Voice > Say
    * advancedボックスライブラリ
        * Audio > Voice > Say Text

3. 1.により作成された入力 `FaceCharacteristics/PersonSmiling` と各ボックスを接続し、内容を以下のように設定します

    ![subscribe-person-smiling-flow.png](https://qiita-image-store.s3.amazonaws.com/0/54394/7818b3fb-309d-4cce-dc82-fb96db89af7d.png)

4. [Say Textボックスのカスタマイズ](http://qiita.com/Atelier-Akihabara/items/8df3e81d286e2e15d9b6#%E8%A3%9C%E8%B6%B3say-text%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA)を参考に、しゃべる内容を変更します

    ```py
    sentence += str(p) + "さん、いい笑顔です！"
    ```


このアプリケーションを実行し、笑顔を向けると、「12743さん、いい笑顔です！」などとしゃべります。一度Pepperから遠ざかり再度近寄ってみたり、別の人が寄ってきて笑顔を向けたりすると、「16234さん、いい笑顔です！」などと別の識別子をしゃべることもわかります。

今回は、説明のために人の識別子をそのまましゃべらせていますが、この識別子は特に利用せず、単純に `FaceCharacteristics/PersonSmiling` をトリガーにして写真を撮影するなどだけでも面白いと思います。ぜひ、活用してみてください。

##People IDごとの SmileProperties の利用

年齢認識と同様に、人の識別子をキーに笑顔度を取得することができます。先の例では意外と笑顔と認識されなかったかもしれませんが、このサンプルを使って自身の笑顔度を確認すると、Pepperにおける笑顔度判定の特性が見えてくるかもしれません。

基本的な構造は、年齢の取得例で紹介した [Get Ageボックス](http://qiita.com/Atelier-Akihabara/items/4162192129f366da1240#alfacecharacteristics-%E5%B9%B4%E9%BD%A2%E3%81%AE%E6%8E%A8%E5%AE%9A) と同様です。
Basic Awarenessボックスによって得られた人のIDから、`PeoplePerception/Person/<ID>/SmileProperties` の値を参照してみます。
ここでは例として、**Basic Awarenessが追跡している人に対して、10秒間隔で笑顔度を取得しその値をしゃべらせる**ということをおこなってみます。

サンプルプロジェクトは **get-smile** です。GitHubから取得したファイルのうち、 `get-smile` フォルダ内の `get-smile.pml` をダブルクリックするなどして開くことができます。
以下のような手順で作成しています。

1. 空のPythonボックスとして、Get Smileボックスを作成します。
    Get Ageボックスと同様、以下のような入出力構成にしています。作成方法は[Pythonボックスの考え方](http://qiita.com/Atelier-Akihabara/items/f6a05ca14be786f9a94c)などを参考にしてください。
    ![get-smile-box.png](https://qiita-image-store.s3.amazonaws.com/0/54394/b64bbebb-4193-4b22-854f-12b0c1eed1d6.png)

2. Get Smileボックスをダブルクリックしてスクリプトエディタを開き、以下のようなPythonスクリプトを記述します

    ```py
    class MyClass(GeneratedClass):
        def __init__(self):
            GeneratedClass.__init__(self)

        def onLoad(self):
            self.memory = ALProxy("ALMemory")
            self.faceChar = ALProxy("ALFaceCharacteristics")

        def onUnload(self):
            pass

        def onInput_onPeopleDetected(self, peopleId):
            if peopleId < 0:
                return
            r = self.faceChar.analyzeFaceCharacteristics(peopleId)
            if not r:
                self.onUnknown()
                return
            smileData = self.memory.getData("PeoplePerception/Person/%d/SmileProperties" % peopleId)
            self.logger.info("Smile Properties: %d => %s" % (peopleId, smileData))
            if smileData and len(smileData) == 2:
                self.onSmile(smileData[0])
            else:
                self.onUnknown()
    ```

    コードの構成はGet Ageボックスと同様です。`ALMemory`から取得しているキーが`AgeProperties`ではなく`SmileProperties`となっている点のみが異なります。

3. Get Ageボックスと同様の手順で、Basic Awarenessボックスと接続します。ここでは、Timerボックスを改造したTimer w/ Numberボックスを使い、最後にBasic Awarenessから出力された値が10秒ごとにGet Smileボックスに入力されるようにしています
    ![get-smile-flow.png](https://qiita-image-store.s3.amazonaws.com/0/54394/d8f39f7f-0152-147d-a2e9-e4ed02fcc120.png)

    各ボックスの値などはプロジェクトファイルの内容を参照してください。

get-smileプロジェクトを実行し、Pepperが顔が見える状態にすると、以下のように10秒おきに笑顔度をしゃべります。
なお、小数点の値をそのまましゃべらせようとすると、「ぜろ てん いち」などとしゃべってしまいわかりにくいので、100倍した整数値をしゃべらせています。

```
あなたの笑顔度は13くらい
あなたの笑顔度は34くらい
```

表情を変化させていくと、笑顔度の値が変化するのがわかります。さまざまな表情をしてみて、Pepperがどのような要素を手がかりに笑顔度を計算しているかを実験してみてください。

なお、[ログビューア]パネルを見ると、笑顔度の生の値と、確度を示す値も見ることができます。

```
[INFO ] behavior.box :onInput_onPeopleDetected:20 _Behavior__lastUploadedChoregrapheBehaviorbehavior_1892204656__root__GetSmile_5: Smile Properties: 47915 => [0.12999999523162842, 0.09800000488758087]
[INFO ] behavior.box :onInput_onPeopleDetected:20 _Behavior__lastUploadedChoregrapheBehaviorbehavior_1892204656__root__GetSmile_5: Smile Properties: 47915 => [0.3400000035762787, 0.8450000286102295]
```

`[0.3400000035762787, 0.8450000286102295]` のように、 `[]` で囲まれた2つの数字が出力されており、1つめの数値が笑顔度、2つめが笑顔度の確度を示す値です。この確度は、セットとなっている笑顔度の数値に関する確信の度合いを示す数値で、0に近いほど自信がない状態、1に近づくほど自信があることを示します。

このように、`PeoplePerception/Person/<ID>/SmileProperties` を参照することで、より詳細な値を取得することができ、細かな制御をおこなうことが可能になります。

#笑顔以外の表情の判定

笑顔の認識と形式は異なりますが、 `PeoplePerception/Person/<ID>/ExpressionProperties` の値を用いることで、人の表情が 無表情(Neutral), 幸せ(Happy), 驚き(Surprised), 怒り(Angry), 悲しみ(Sad) のいずれに見えるかどうかを判別することが可能です。
`SmileProperties` によって得られる情報は、 `[笑顔度, 確度]` の形式でしたが、 `ExpressionProperties` の値は、 `[無表情, 幸せ, 驚き, 怒り, 悲しみ]` の順で、それぞれの表情として判断される度合いが0.0～1.0の範囲で格納されます。また、これらの値の総和は1.0となるようになっています。

この値を使うサンプルをサンプルプロジェクト **get-expression** として用意しています。GitHubから取得したファイルのうち、 `get-expression` フォルダ内の `get-expression.pml` をダブルクリックするなどして開くことができます。
以下のような手順で作成しています。

1. 空のPythonボックスとして、Get Expressionボックスを作成します
    
    ![get-expression.png](https://qiita-image-store.s3.amazonaws.com/0/54394/c0e4b964-b11c-624f-b351-d19485b46ee8.png)
    
    なお、onMax出力に出力される値は、 0...neutral, 1...happy, 2...surprised, 3...angry, 4...sad となります。
    
2. Get Expressionボックスとして以下のようなコードを記述します

    ```py
    class MyClass(GeneratedClass):
        def __init__(self):
            GeneratedClass.__init__(self)

        def onLoad(self):
            self.memory = ALProxy("ALMemory")
            self.faceChar = ALProxy("ALFaceCharacteristics")

        def onUnload(self):
            pass

        def onInput_onPeopleDetected(self, peopleId):
            if peopleId < 0:
                return
            r = self.faceChar.analyzeFaceCharacteristics(peopleId)
            if not r:
                self.onUnknown()
                return
            exprData = self.memory.getData("PeoplePerception/Person/%d/ExpressionProperties" % peopleId)
            self.logger.info("Expression Properties: %d => %s" % (peopleId, exprData))
            if exprData and len(exprData) == 5:
                self.onNeutral(exprData[0])
                self.onHappy(exprData[1])
                self.onSurprised(exprData[2])
                self.onAngry(exprData[3])
                self.onSad(exprData[4])
                self.onMax(exprData.index(max(exprData)))
            else:
                self.onUnknown()
    ```

3. テスト用に、たとえば以下のようなフローを記述します。onNeutral～onSadの各出力は動作確認のためのログ出力をおこなうLogボックスを、onMaxの値に応じて「幸せそうですね。」「びっくりしましたか。」などとしゃべらせる内容を変更するようなSwitch Caseボックスを配置しています

    ![get-expression-example.png](https://qiita-image-store.s3.amazonaws.com/0/54394/6fe84ef2-9c8c-f5c4-297d-1bd89976193a.png)

このようにして、`ExpressionProperties` の各値を取得したり、最も大きな値が得られた項目に応じてしゃべらせることなどができます。

たとえば、サンプルプロジェクトを実行し、口を開けて目を見開いて、驚いたような顔を作ると、ログに以下のように出力されます。Surprisedの項目が大きな値になっていることがわかります。

```
[INFO ] behavior.box :onInput_onPeopleDetected:20 _Behavior__...: Expression Properties: 60331 => [0.14000000059604645, 0.0, 0.85999995470047, 0.0, 0.0]
[INFO ] behavior.box :onInput_message:27 _Behavior__...: Neutral: 0.140000000596
[INFO ] behavior.box :onInput_message:27 _Behavior__...: Happy: 0.0
[INFO ] behavior.box :onInput_message:27 _Behavior__...: Surprised: 0.8599999547
[INFO ] behavior.box :onInput_message:27 _Behavior__...: Angry: 0.0
[INFO ] behavior.box :onInput_message:27 _Behavior__...: Sad: 0.0
```

また、`ExpressionProperties` の値のうち3番目の数値、驚きの値が最大値なので、onMax出力には `2` が出力されます。
この値がSwitch Caseボックスに入力され、「びっくりしましたか。」としゃべる処理が実行されます。

このように、`PeoplePerception/Person/<ID>/ExpressionProperties` を利用することで、笑顔以外の表情についても判定することが可能です。ここまで見てきた、笑顔などの表情の情報を用いることで、Pepperに利用者の感情に応じた動きをおこなわせることが可能になります。