こんにちは、海行です。
Doge CameraというAndroidアプリを作りました。
端末のカメラで見えている映像に対して、リアルタイムにDogeがDoge風にコメントしてくれるという物です。
わかりやすいように実際にコメントをボイスで読み上げる機能も付けました。
スクリーンショットを撮影、保存する事もできます。
Dogeとは? → 参考リンク:2013年に大流行のミーム Doge とは
Unity3DとGoogle Cloud Vision APIを使用して制作しました。
Cloud Vision APIが限定プレビュー版なので利用制限があるため、残念ながらアプリ配布はまだ行えません。
一か月ほど前にGoogleがCloud Vision APIを発表した時に真っ先にこれが思いつきました。
これの何が面白いかというと、今見えている風景に対してコンピュータがそこに何が映っているのかを認識してくれます。
これまでも人間の顔を認識する事くらいは出来ましたが、自転車を見たら自転車、アニメを見たらアニメだという風に多様な物を識別できるというのは画期的ではないでしょうか。
↓このDoge Cameraを公園に持って行った様子のプレイ動画です。
アプリに表示されてるDogeの線画はWulcanisさんからお借りしました。
リンク:Shibe Lineart ( Free to use) REUPLOADED: MORE DOGE
技術的な話
このアプリはGoogle Cloud Vision APIを試しに使って何か作ってみようという趣旨で制作しました。
Google Cloud Vision APIを使うと何が出来るのかというのはこちら(Node.jsとGoogle Cloud Vision API使って色々な画像認識試してみた。)の記事に詳しいです。こちら(Cloud vision apiを使ってみた。)でも各機能を試されてます。
すでに色々情報が上がってますが、Unity3Dを使ってAndroidからCloud Vision APIを使用という点は史上初っぽいので、その辺をざっくり解説します。
やってる事は非常に簡単です。
①リクエスト用のJSONを作る
②HTTPリクエストでAPIを叩く
③レスポンスJSONからデータをゲット
①リクエスト用のJSONを作る
Cloud Vision APIはJSONでリクエストを出してJSONでレスポンスを受け取るので、リクエスト用のJSONを作りましょう。
UnityでJSONを扱う時は私は以前はLitJsonを使ってましたが、Unity5.3でUnity公式でJSONがサポートされたのでそっちを使ってみます。
UnityのJsonUtilityはDictionary(というかインスペクタに出てこないもの)はシリアライズしてくれないようです。
LitJsonは確か入れ子のDictionaryとかも何なくシリアライズしてくれて簡単だったのですが、JsonUtilityは不便な分高速だという話なので、我慢して使ってみます。
参考リンク:Unity 5.3で実装されたJsonUtilityの注意点まとめ
JSONをシリアライズするためのクラスを設計します。
ドキュメントにクラス設計が書いてるのでそれ通りにクラスを書いていくべきなのですが、今回は動けばそれでいいのでリクエストJSONの例を見てパパッと作っちゃいます。
↓これがドキュメントのGETTING STARTEDに載ってたリクエストJSONの例です
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ "requests":[ { "image":{ "content":"base64-encoded file data" }, "features":[ { "type":"LABEL_DETECTION", "maxResults":1 } ] } ], } |
これ通りに設計すればOKですが、気を付けるポイントは、[]は配列になってる事と、””に囲まれてたら文字列ですが囲まれてなかったら数値だという所です。
設計したクラスはこんな感じです。
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 |
//-----------REQUEST-------------- [System.Serializable] public class RequestClass { public List<Requests> requests; } [System.Serializable] public class Requests { public Images image; public List<Features> features; } [System.Serializable] public class Images { public string content; } [System.Serializable] public class Features { public string type; public int maxResults; } |
②HTTPリクエストでAPIを叩く
以前のUnityだとHTTP通信するにはWWWクラスを使ってましたが、新しくUnityWebRequestクラスができてます。
WWWがGETとREST通信にしか対応してないのに対してUnityWebRequestはPUTやDELETEにも対応してて上位互換なので新しい方を使いたいですね。
参考リンク:UnityWebRequestについてちょっと調べてみた
ところがどっこい、実際に使ってみたところ何故か400番の不正なJSONエラーが出てしまいました。
WWWだと普通に通信が通るので、しょうがないので今回はWWWで通信します。
叩くURLは
https://vision.googleapis.com/v1alpha1/images:annotate?key=
↑Cloud Vision用APIキーをGoogleデベロッパーコンソールから入手して上の末尾に足した物を叩きます。
③レスポンスJSONからデータをゲット
レスポンスで返ってきたJSONをデータにします。
リクエストJSON用のクラスを設計したのと同じ要領です。
↓ドキュメントのGETTING STARTEDに載ってたレスポンスJSONの例です
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ "responses": [ { "labelAnnotations": [ { "mid": "/m/0bt9lr", "description": "dog", "score": 0.89208293 } ] } ] } |
↓JSONから設計したクラスは以下のような感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//----------RESPONSE-------------- [System.Serializable] public class ResponseClass { public List<Response> responses; } [System.Serializable] public class Response { public List<LabelAnnotation> labelAnnotations; } [System.Serializable] public class LabelAnnotation { public string mid; public string description; public float score; } |
最後に、①~③の部分の処理を行っているソースコードを抜粋して掲載します。
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 |
IEnumerator coroutineSendHttpRequest() { while (true) { yield return new WaitForSeconds(3f); //リクエスト作成 RequestClass requestClass = new RequestClass(); Requests requests = new Requests(); requestClass.requests = new List<Requests>(); requestClass.requests.Add(requests); //webcamtextureをbase64エンコードテキストに変換 _snapTexture.SetPixels(_webCamTexture.GetPixels()); _snapTexture.Apply(); byte[] bytesJpg = _snapTexture.EncodeToJPG(); //クオリティ下げたら送信サイズ下がる requests.image = new Images(); requests.image.content = System.Convert.ToBase64String(bytesJpg); requests.features = new List<Features>(); Features feature = new Features(); feature.type = "LABEL_DETECTION"; feature.maxResults = 10; requests.features.Add(feature); string jsonData = JsonUtility.ToJson(requestClass, false); //Debug.Log(jsonData); string url = "https://vision.googleapis.com/v1alpha1/images:annotate?key="; url += windowsApiKey; Dictionary<string, string> header = new Dictionary<string, string>(); header.Add("Content-Type", "application/json; charset=UTF-8"); byte[] postData = System.Text.Encoding.Default.GetBytes(jsonData); WWW www = new WWW(url, postData, header); yield return www; // 成功 if (www.error == null) { //Debug.Log("Post Success\n" + www.text); //レスポンスを解析 ResponseClass responseClass = JsonUtility.FromJson<ResponseClass>(www.text); //信頼度0.7以上のラベルをリストに突っ込む if (responseClass != null) { for (int i = 0; i < responseClass.responses.Count; i++) { Response response = responseClass.responses[i]; for (int j = 0; j < response.labelAnnotations.Count; j++) { if (response.labelAnnotations[j].score >= 0.7f) { LabelToken labelToken = new LabelToken(); labelToken._lifeTime = 10f; labelToken._label = response.labelAnnotations[j].description; labelToken._score = response.labelAnnotations[j].score; _labelTokenList.Add(labelToken); } } } } } // 失敗 else { Debug.Log("Post Failure"); } /*var request = UnityWebRequest.Post(url, jsonData); request.SetRequestHeader("Content-type", "application/json; charset=UTF-8"); Debug.Log(request.GetRequestHeader("Content-type")); yield return request.Send(); Debug.Log(request.responseCode.ToString() + ":" + request.downloadHandler.text);*/ } } |
こんな感じになります。
下の方のコメントアウトしてる箇所は動かなかったUnityWebRequestで書いてた部分です。
誰か動かない謎がもしわかれば教えてください。
今後の展望
実際に試してみてGoogle Cloud Vision APIがどんなものか大体わかりました。
Cloud Vision APIはディープラーニングの技術が使われているので、ディープラーニングによる画像認識もこういう感じかなと掴めたのでディープラーニング初心者が取っ付いてみるという意味でも面白かったです。
Cloud Vision APIはまだ限定プレビュー版なので正式リリースまではこれを使って製品を出したりは出来ませんが、リリースされたら有料になるんだろうなあ。
今回使ったのはラベルディテクションという画像に何が写ってるか識別する機能ですが、画像を投稿するサービスだとセーフサーチ機能を利用したり、レシート手動入力サービスだと画像OCRの機能を利用していきそうですね。
フォルダの中にある大量の画像を自動で仕分けするみたいな使い方もできそうです。
これからはコンピュータがどんどん世界を認識していくんだなあという予感を抱きました。
今はまだ人を見てもpersonという事までしかわからないみたいですが、いずれ顔を見たらそれが誰かまでわかるようになるでしょう。
コンピュータが”まなざし”を持つようになるという事ですね。
突き詰めていくと何が起こるのか…それはまだわかりません。
何か面白い使い道のアイデアがあれば、みなさんも是非Google Cloud Vision APIを試してみてください。
無料で使えるのは限定プレビューの今だけかも!?