のしメモ アプリ開発ブログ

Unityアプリとかロボットとか作ってるときに困ったこととかメモ

Actions on Googleを始める

Actions on Google

音声を使用した会話形式のアプリが作れるツールです。
Google Homeのアプリもシミュレータで確認しながら作れるみたいなので試してみました。

サンプルではAPI.AIのNode.jsを使ったアプリで、Firebaseを使ってデプロイできるようになっています。
今回は公式のGetStartedをやりながら手順をメモしたものになります。

参考資料について

公式のGet startedを参考に進めていきます。公式マニュアルを読みながらの方が、最新で細かいところも書いているのでおすすめです。
https://developers.google.com/actions/get-started/


実行手順

1. ActionsのプロジェクトとAPI.AIエージェントを作成

(1) サンプルアプリケーションを導入

https://github.com/actions-on-google/apiai-facts-about-google-nodejs
サンプルのフォルダを作成します。(今回は`SampleAction`というフォルダ名とする)
`SampleAction/FactsAboutGoogle.zip`
`SampleAction/functions/index.js`
というディレクトリ階層になるように配置します。

(2) Activity controlsから権限を編集

Chromeの下記の権限を有効に設定します。
・ウェブとアプリのアクティビティ
・音声アクティビティ
・端末情報
編集はこちらから。
https://myaccount.google.com/activitycontrols?pli=1
f:id:noshipu:20170717025103p:plain

(3) プロジェクトとAgentを作成する

a. Actions on Google Developer Consoleに移動します
b. `Add Project`から`FactsApp`を選択しプロジェクト名をSampleActionと入力、regionをJapanに設定し、`Create Project`を選択します。
c. `Use API.AI`から`CREATE ACTIONS ON API.AI`を選択しActionsをビルドします。
d. API.AIコンソールが立ち上がり、Agentの画面でそのままSaveを押して情報を保存します。

(4) FactsAppのAgentにサンプルプロジェクトを復元

a. 左側のナビゲーションバーにある、Agent名の右にある歯車アイコンを押します。

f:id:noshipu:20170717141911p:plain

b. `Export and Import`タブを選択します。
c. `RESTORE FROM ZIP`を押し、先ほど配置した`SampleAction/FactsAboutGoogle.zip`を選択します。
d. RESTOREとテキストボックスに入力し、`RESTORE`ボタンを押し、その後`DONE`ボタンを押します。

2. fulfillmentをデプロイ

firebaseを使用してデプロイします

(1) Node.jsをインストールする

後述しますが、v6以上のインストールが必要です。
※Node.jsのインストール手順説明は飛ばします。

(2) firebaseクライアントソフトをインストー

npm install -g firebase-tools

(3) Googleアカウントでログイン

下記を実行しGoogleにログイン

firebase login

(4) firebaseをActionsのプロジェクトに紐付けます

a. {PROJECT_ID}を確認する

`firebase list`を叩くと先ほど作ったプロジェクトのIDを確認することが可能です。
先ほどの管理ページからも確認することが可能です。

b. プロジェクトに紐付ける
cd SampleAction/functions
firebase use {PROJECT_ID}

`Now using project {PROJECT_ID}`と表示されたら成功です。

(5) デプロイ開始

下記コマンドを実行します

cd SampleAction/functions
npm install
firebase deploy --only functions

=================実行時にエラーが起きた場合=================
実行時にNode.jsのバージョンが古く、下記のメッセージが表示されたのでアップデート。

Error: There was an unknown problem while trying to parse function triggers. Please ensure you are using Node.js v6 or greater.

Node v8.0.0だと下記のエラーが

TypeError: Cannot read property 'pipesCount' of undefined

v8.1.4にあげて再度実行すると成功しました。
=================実行時にエラーが起きた場合ここまで=================

デプロイに成功するとこのようなメッセージが表示されます。

✔  Deploy complete!

Project Console: {PROJECT CONSOLE URL}
Function URL (factsAboutGoogle): {FUNCTION URL}

{FUNCTION URL}をコピーしておく

(6) URLを設定

a. API.AIのページに戻る
b. 左側のナビゲーションバーから`Fulfillment`を選択し`ENABLED`にする
c. URLの入力を求められるので{FUNCTION URL}を入力し`SAVE`を押す

f:id:noshipu:20170717152129p:plain
Basic認証を設定することも可能です。ローカル開発機を使いたい場合はngrok等のツールを使えば可能だが、脆弱性に気をつける必要があるとのことです。

3. シミュレータを動かす

(1) API.AIのページの左側のナビゲーションバーから`Integrations`を選択

(2) Actions on GoogleのカードがOnになっていることを確認

f:id:noshipu:20170717153312p:plain

(3) そのままカードをクリック

(4) そのままカードをクリックして`TEST`ボタンをクリック

`TEST`ボタンが存在しない場合は`AUTHORIZE`ボタンを押してから`TEST`をクリックします。
`TEST`を押すとAgentがアップロードされてシミュレータでテストできるようになります。

(5) `Test your Assistant app`の項目の`VIEW`をクリック

(6) シミュレータのWebページが開くので、`START`をクリック

(7) シミュレータのチャットのテキストボックスに入力

`talk to my test app`を入力するとこんな形でメッセージが返ってきたらサンプルの実行成功です。

Welcome to Facts about Google! Do you want to hear about Google's history or do you want to hear about its headquarters?

(8) アプリを使ってみる

下記のメッセージを送るとIntentを呼び出して試せます。
`talk to Facts about Google about cats`
`talk to Facts about Google`
`talk to Facts about Google about Google"s history`

日本語の設定ができたりもするので、Zip内の設定ファイルを編集して色々試してみようと思います。

何か間違いや、問題があればお気軽にコメント等ください。

GoogleTangoアプリ「Makebox AR」をリリースしました

GoogleのAR技術Tangoを利用したARアプリをリリースしました。

無料です!
play.google.com


アプリの内容はこんな感じ

簡単に3Dモデルを作って、UnityとかUE4にモデルデータを持ってくることが可能です。

実装について

アプリ自体はUnityとTangoSDK(Unity版)で作られています。

ラッキング周り等もあまり意識せずに実装できました。
ラーニングして読み込ませて等をやるとちょっとめんどくさそうですが、簡易的なARならカメラのPrefabを置くだけレベルでさくっと実装ができる感じです。

UnityでのシンプルAR実装方法はこちらから - Unity How-to Guide: Simple Augmented Reality
https://developers.google.com/tango/apis/unity/unity-simple-ar


Unityでスマホ開発したことある人であればuGUIの操作や、Raycastを飛ばして~など、変わらない形なので、簡単にTangoアプリが作れると思いますので、是非作ってみてください!

GooglePlayでTango対応端末にだけリリースする

Tangoの機能が使えるかの判定をアプリ内で行うことができるので、そちらで確認し処理を分岐することも可能ですが、
今回は、Tango端末にのみ配布できる設定をする方法を紹介します。
手動で制御もできなくもないですが、今後の端末追加等を考えると今回の設定をしておいたほうがよさそうです。

やり方

Manifestファイルに1行追加するだけで対応端末が自動で絞ることが可能です。
AndroidManifest.xmlを編集
"application"に"user-library"を1つ追加するだけ

    <application ...>
        ...
        ...

        <uses-library android:name="com.projecttango.libtango_device2" android:required="true" />
    </application>
</manifest>

ビルドしたapkをアップロードすると...

サポートされている端末が3つになります。(2017/06/20現在)
f:id:noshipu:20170620193248p:plain


逆に言えば、この設定をしてしまうと、Tango搭載端末にしか配布できないので、Tangoが使えない場合の処理を入れたapkを配布したい場合はこの設定は外してしまって問題ないです。

Unityで学ぶ画像処理【色の変換編】

Unityにはピクセルごとに色を取得することができるので、Unityは画像処理もできるのです。
以前の記事はこちらです。Texture2D書き換え周りの基本的な処理はこちらをご参考に。

今回はInterfaceの5月号の画像処理特集のアルゴリズムを読みながら、Unityでコードを書いて学んでいきたいと思います。

こんな人におすすめ

・画像処理の仕組みを学びたい
・様々なプラットフォームで、スタンドアロンで動作する画像処理がしたい

今回はUnityのTexture2Dを使った画像処理について紹介していきます。
リアルタイムで行う場合はPostProcessingやShaderで処理するとよいですが、今回は説明しません。

ちなみに、360度画像からおっさんを消す「VANISH360」もUnityのTexture2Dクラスを使っておっさんを消しています。

RGB色入れ替え

実行結果

R->G, G->B, B->R

f:id:noshipu:20170524215431p:plain

R->B, G->R, B->G

f:id:noshipu:20170524215920p:plain

コード

R->G, G->B, B->R

Color[] inputColors = inputTexture.GetPixels();
Color[] outputColors = new Color[width * height];
for (int y = 0; y < height; y++)
{
    for (int x = 0; x < width; x++)
    {
        var color = inputColors[(width * y) + x];
        outputColors[(width * y) + x] = new Color(color.g, color.b, color.r);
    }
}
outputTexture.SetPixels(outputColors);
outputTexture.Apply();

解説

RGBの色を入れ替えるので、結構大きく色調が変わるものの、大きくバランスが崩れることはない感じです。

モノクロエフェクト

実行結果

f:id:noshipu:20170524222618p:plain

コード

Color[] inputColors = inputTexture.GetPixels();
Color[] outputColors = new Color[width * height];
for (int y = 0; y < height; y++)
{
    for (int x = 0; x < width; x++)
    {
        var color = inputColors[(width * y) + x];
        float average = (color.r + color.g + color.b) / 3;
        outputColors[(width * y) + x] = new Color(average, average, average);
    }
}
outputTexture.SetPixels(outputColors);
outputTexture.Apply();

解説

RGB色の平均値を計算し、全ての色の同一の強さにすることでモノクロにすることができます。
画素値が同一の強さになることで、強調する色がなく、モノクロの状態にすることができます。

セピアエフェクト

実行結果

f:id:noshipu:20170524223254p:plain

コード

Color[] inputColors = inputTexture.GetPixels();
Color[] outputColors = new Color[width * height];
for (int y = 0; y < height; y++)
{
    for (int x = 0; x < width; x++)
    {
        var color = inputColors[(width * y) + x];
        float average = (color.r + color.g + color.b) / 3;
        outputColors[(width * y) + x] = new Color(average, average * 0.8f, average * 0.55f);
    }
}
outputTexture.SetPixels(outputColors);
outputTexture.Apply();

解説

モノクロの拡張版です。
モノクロの状態である平均値に対して、セピアの明度を各色に乗算してあげることでセピアな感じを出すことができます。
モノクロ画像をセピアにするには(Rx1, Gx0.8, Bx0.55)を乗算する。

ポスタリゼーションエフェクト

実行結果

split=5

f:id:noshipu:20170524231006p:plain

split=2

f:id:noshipu:20170524231114p:plain

split=3 && モノクロ

f:id:noshipu:20170524231607p:plain

コード

Color[] inputColors = inputTexture.GetPixels();
Color[] outputColors = new Color[width * height];

int split = 3;
for (int y = 0; y < height; y++)
{
    for (int x = 0; x < width; x++)
    {
        var color = inputColors[(width * y) + x];
        for (int i = 0; i < split; i++)
        {
            float col1 = i * (1f / (float)split);
            float col2 = (i + 1f) * (1f / (float)split);
            if(col1 <= color.r && color.r <= col2)
            {
                color.r = (col1 + col2) / 2f;
            }
            if (col1 <= color.g && color.g <= col2)
            {
                color.g = (col1 + col2) / 2f;
            }
            if (col1 <= color.b && color.b <= col2)
            {
                color.b = (col1 + col2) / 2f;
            }
        }
        outputColors[(width * y) + x] = color;
    }
}
outputTexture.SetPixels(outputColors);
outputTexture.Apply();

解説

ポスターっぽく。漫画エフェクトみたいな画像処理です。
色の段階を減らすことによって、色のバリエーションが少なくなり、ポスターやイラストっぽい雰囲気を出すことができます。
splitで0から1で設定できる色調を区切りをつけて設定させます。

ソラリゼーションエフェクト

実行結果

f:id:noshipu:20170525020839p:plain

コード

Color[] inputColors = inputTexture.GetPixels();
Color[] outputColors = new Color[width * height];
for (int y = 0; y < height; y++)
{
    for (int x = 0; x < width; x++)
    {
        var color = inputColors[(width * y) + x];
        if(color.r <= 0.5f)
        {
            color.r = 2 * color.r;
        }
        else
        {
            color.r = 1 * 2 - 2 * color.r;
        }
        if (color.g <= 0.5f)
        {
            color.g = 2 * color.g;
        }
        else
        {
            color.g = 1 * 2 - 2 * color.g;
        }
        if (color.b <= 0.5f)
        {
            color.b = 2 * color.b;
        }
        else
        {
            color.b = 1 * 2 - 2 * color.b;
        }
        outputColors[(width * y) + x] = color;
    }
}
outputTexture.SetPixels(outputColors);
outputTexture.Apply();

解説

画素値の差を強調できる処理で、画素値が高いものは色の反転を起こし一部がネガっぽい感じになっています。
複数回を折り返すパターンもできるので、結果がどのよう状態になるのか、予測がなかなかつかないので楽しいですね。

モザイクエフェクト

実行結果

size=11

f:id:noshipu:20170525025152p:plain

size=27

f:id:noshipu:20170525025333p:plain

size=151

f:id:noshipu:20170525025435p:plain

コード

Color[] inputColors = inputTexture.GetPixels();
Color[] outputColors = new Color[width * height];

int size = 151;
for (int y = (size - 1) / 2; y < height; y = y + size)
{
    for (int x = (size - 1) / 2; x < width; x = x + size)
    {
        float colorR = 0f;
        float colorG = 0f;
        float colorB = 0f;

        for (int j = y - (size - 1) / 2; j <= y + (size - 1) / 2; j++)
        {
            for (int i = x - (size - 1) / 2; i <= x + (size - 1) / 2; i++)
            {
                if (i >= 0 && j >= 0 && i < width && j < height)
                {
                    colorR += inputColors[(width * j) + i].r;
                    colorG += inputColors[(width * j) + i].g;
                    colorB += inputColors[(width * j) + i].b;
                }
            }
        }

        colorR = colorR / (size * size);
        colorG = colorG / (size * size);
        colorB = colorB / (size * size);

        for (int j = y - (size - 1) / 2; j <= y + (size - 1) / 2; j++)
        {
            for (int i = x - (size - 1) / 2; i <= x + (size - 1) / 2; i++)
            {
                if (i >= 0 && j >= 0 && i < width && j < height)
                {
                    outputColors[(width * j) + i] = new Color(colorR, colorG, colorB);
                }
            }
        }
    }
}
outputTexture.SetPixels(outputColors);
outputTexture.Apply();

解説

指定のピクセルから周辺の色を取得し、平均値を計算して周辺のピクセルに割り当ててあげることで、ブロック単位で色が変わりモザイクになります。

コードはGithubで公開してます

今回使用したUnityプロジェクトはGithubにのっけています。今後も更新予定です。

最後に

画像処理楽しい!
続きも近いうちにやります。