読者です 読者をやめる 読者になる 読者になる

RaspberryPiで Cotogoto::Noby APIを使ってみる【ヒミツのクマちゃん その1】

API RaspberryPi python 開発 電子工作

RaspberryPiでCotogoto::Noby APIを使ってみる

前回のエントリではかなり尻切れトンボのような感じで終わってしまったのですが 今回はその続きになります。

今回もpythonからのアクセスになるので定番のライブラリであるrequestsを使用することになります。Snipetのサンプルである占いの機能を実装させてみます。

Noby APIでは会話だけでなくおみくじをひくことができます。API界隈では占いっぽいものもあるのですが、ほとんどが有料なのでそれに近いおみくじの機能はなかなかイケてると思っています。

以下のように記述します。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import requests
import json
import types

ENDPOINT = 'https://www.cotogoto.ai/webapi/noby.json'
MY_KEY = '【取得したAPIKEY】'

payload = {'text': 'おみくじ引きたいな。', 'app_key': MY_KEY}
r = requests.get(ENDPOINT, params=payload)
data = r.json()

response = data['text']
print "response: %s" %(response)

DoCoMoの雑談対話APIではPOSTメソッドでリクエストを送っていましたが、Noby APIではGETメソッドを使用するので敷居はかなり低くなります。特にArduinoやmbedなどでもサンプルに類似した形式で簡単に記述ができそうです。

送信したデータのtextにおみくじの内容が格納されているのでそれ使用することになります。 実際には以下のようなJSONデータがレスポンスとして返されます。

{
  "art": null,
  "commandId": "NOBY_004",
  "commandName": "おみくじ",
  "dat": "おはようございます",
  "emotion": {
    "angerFear": 0,
    "joySad": 0,
    "likeDislike": 0,
    "word": null
  },
  "emotionList": [],
  "loc": null,
  "mood": 0,
  "negaposi": 0,
  "negaposiList": [
    {
      "score": -0.30000001192092896,
      "word": "引き"
    }
  ],
  "org": null,
  "psn": null,
  "text": "小吉\r\nあなたの心を平和にして、他人のためにつくせば吉です。\r\nまずはあなたの家庭を平和にしましょう。\r\nそして、あなたの周りからだんだんに平和な気持ちを広げていきます。\r\nそうすれば、周りにどんな波風が立っても、あなたとその周囲だけには春風が吹きます。\r\nその春風を、また周囲に広げていきましょう。\r\n【願い事】他人のため、という気持ちがあればかないます。\r\n【待ち人】遅れますが来ます。\r\n【失し物】男の人に聞いてみましょう。\r\n【旅行】早い旅立ちがいいでしょう。\r\n【ビジネス】いいでしょう。\r\n【学問】もうちょっと勉強しましょう。\r\n【争い事】人に頼んだほうがうまくいきます。\r\n【恋愛】再出発もいいかも知れません。\r\n【縁談】あなたの心が晴れやかならば疑いは晴れてうまくいきます。\r\n【転居】問題ありません。\r\n【病気】いい医師に出会うよう努力しましょう。そうすれば治ります。",
  "tim": null,
  "type": "Command",
  "wordList": [
    {
      "feature": "名詞,一般,*,*,*,*,おみくじ,*,*,",
      "start": "0",
      "surface": "おみくじ"
    },
    {
      "feature": "動詞,自立,*,*,五段・カ行イ音便,連用形,引く,ヒキ,ヒキ,",
      "start": "1",
      "surface": "引き"
    },
    {
      "feature": "助動詞,*,*,*,特殊・タイ,基本形,たい,タイ,タイ,",
      "start": "2",
      "surface": "たい"
    },
    {
      "feature": "助詞,終助詞,*,*,*,*,な,ナ,ナ,",
      "start": "3",
      "surface": ""
    },
    {
      "feature": "記号,句点,*,*,*,*,。,*,。,",
      "start": "4",
      "surface": ""
    }
  ]
}

若干の難点としては結構文面が長いということでしょうか。文面は\r\nで区切られてるので、後は分割すれば使えます。個人的にはおみくじの結果よりも2文節目のフレーズのほうが占いっぽいのでいいかなと思います。

使えたけど…あんまりこれだけでは面白くない。

これではこれまでのサンプルをそのまま使っただけの糞エントリーになってしまうので、今回はもう少し発展させてみたいと思います。

今回はRaspberryPiにつけたカメラで人間の顔が認識できたら、おみくじを引いてフレーズを音声として出力させてみたいと思います。

顔の認識に関しては

uepon.hatenadiary.com

発話(TextToSpeach)に関しては

uepon.hatenadiary.com

といった過去のエントリーを思い出して行うことになります。

そして今回の一番の目玉は音声出力を行う「ヒミツのクマちゃん」になります。

これでぐっとサービスっぽくなります。本当は「BOCCO」とかが入手できるとまた違ったこともできるんですが… 「ヒミツのクマちゃん」はハッカソンで同じチームになった小林さんに教えてもらいました。ありがとうございます。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import requests
import json
import types
import time
import picamera
import os
import sys

SHORTSLEEP = 10
LONGSLEEP = 60

try:
  while True:
        # MS Emotion APIで顔の認識を行う
        with picamera.PiCamera() as camera:
                camera.start_preview()
                camera.hflip = True
                camera.vflip = True
                time.sleep(2) #カメラ初期化
                camera.capture('foo.jpg')

        url = 'https://api.projectoxford.ai/emotion/v1.0/recognize'
        headers = {'Content-type': 'application/octet-stream', 'Ocp-Apim-Subscription-Key':'【Emotion APIのキー】'}
        payload = open('./foo.jpg', 'rb').read()

        r = requests.post(url, data=payload, headers=headers)
        data = r.json()

        ninshiki = len(data)

        # 顔認識が出来たらNoby APIにアクセスしておみくじを引く

        if ninshiki > 0:
                dict = data[0]['scores']
                max_key = max(dict, key=(lambda x: dict[x]))

                print "key: %s,\t value:%f" %(max_key, dict[max_key])

                ENDPOINT = 'https://www.cotogoto.ai/webapi/noby.json'
                MY_KEY = '【Noby APIのキー】'

                payload = {'text': 'おみくじ引きたいな。', 'app_key': MY_KEY}
                r = requests.get(ENDPOINT, params=payload)
                data = r.json()

                response = data['text']
                # print "response: %s" %(response)

                uranai = response.split("\r\n")[1]
                print uranai

                # おみくじのフレーズを話す
                voice1 = 'こんにちは、今日は来てくれてありがとう。'
                voice2 = '今日の運勢を占うね。'

                # shを呼び出す。
                cmdtext = './jtalk.sh ' + uranai
                # print cmdtext

                os.system('./jtalk.sh ' + voice1) #成功すると0 # import os
                os.system('./jtalk.sh ' + voice2) #成功すると0 # import os
                os.system(cmdtext.encode('utf-8')) #成功すると0 # import os
                print 'LONG_SLEEP_MODE'
                time.sleep(LONGSLEEP)
        else:
                print 'SHORT_SLEEP_MODE'
                time.sleep(SHORTSLEEP)

except KeyboardInterrupt:
  sys.exit()

pythonから呼び出しているjtalk.shのshは以下の通り

#!/bin/bash

HTSVOICE=/usr/share/hts-voice/nitech-jp-atr503-m001/nitech_jp_atr503_m001.htsvoice
#HTSVOICE=/usr/share/hts-voice/mei/mei_normal.htsvoice

voice=`tempfile`
option="-m $HTSVOICE \
  -s 16000 \
  -p 100 \
  -a 0.03 \
  -u 0.0 \
  -jm 1.0 \
  -jf 1.0 \
  -x /var/lib/mecab/dic/open-jtalk/naist-jdic \
  -ow $voice"

if [ -z "$1" ] ; then
  open_jtalk $option
else
  if [ -f "$1" ] ; then
    open_jtalk $option $1
  else
    echo "$1" | open_jtalk $option
  fi
fi

aplay -q $voice
rm $voice

処理の流れとしては以下の様になっています。

  • RaspberryPIのカメラモジュールで画像を撮影
  • 撮影した画像データをEmoion APIへOctetStreamで送信し解析
  • 画像内に人の顔があれば、NobyAPIへおみくじを要求。
  • 画像内に人がいなければ、短時間のスリープ
  • おみくじ結果が得られたらjtalk.sh(OpenJTalkで話す機能をもつシェルスクリプト)の引数におみくじの結果をいれて長時間のスリープ
  • 以上を繰り返す

ネットワーク越しにAPIを叩いているので遅延があることは覚悟する必要はあります。気になる場合には機器を設置するシチュエーションでカバーすれば感覚として軽減はできそうです。(椅子を置いて座ってもらうなど)

実際に動かした状態は以下で


クマ占い

カメラの設置位置が難しいですが、専用の椅子やリュックなどを作成して自然に見せるのも楽しいかなと思います。