Posts Tagged: android

OpenCV(Android)で顔認識した結果のテクスチャをUnityで表示する

はじめに

UnityでOpenCVを使用する場合、
AssetStoreで販売されているOpenCV for Unityを使用するか、
自前でC++などでUnityネイティブプラグインを開発するかの2つ方法があります。
OpenCV for Unityは自分は使ったことはありませんが、言及しているブログも多く、
マルチプラットフォームそれぞれ用にネイティブプラグインを作るのが面倒くさかったりする場合は
お金で解決してしまった方が手っ取り早いかもしれません。

さて、今回の内容は、
いやAssetStoreに頼らず自分でネイティブプラグイン作ってみたい!
とか、お金がない!
などといった事情をお持ちの方向けとなってます。

構成

Unity側でAndroidの機能を使うにはいくつか方法があります。
Android用プラグインのビルドと使用
ここにまとまっています。

このプロジェクトでは
AAR プラグインと Android ライブラリ(1)

UnityPlayerActivity Java コードの拡張(2)
の2つのドキュメントで述べられている機能を使います。

Unity(C#)からAndroid(Java)のコードを呼ぶには(1)だけやればよいのですが、
今回はAndroid(Java)側が起動したカメラ画像を使用したかったため(2)が必要でした。
Unityでカメラ画像使いたいならWebCamTextureが簡単じゃん、と思うのですが今回は後述の理由のため使用していません。

構成としてはこの様になっています。

Android
|- OpenCVでカメラ画像を取得/顔検出
|- OpenGLでテクスチャを生成
Unity
|- テクスチャを受け取り表示

また、今回使用したバージョンは次の通りです。

Unity 2017.4.0f1 Perfonal(64bit)
Android Studio 3.0.1
Android OpenCV library 3.4.1

チュートリアル

簡単ですが作成方法を記載します。
プロジェクトはGithubに上げています。
詳細はこちらでご確認ください。

Unityプロジェクトの作成

まずはUnityプロジェクトを作成します。
シーンにはカメラ画像を表示するためのQuadを追加したのみです。
このQuadにはネイティブプラグイン側の機能を呼び出すためのスクリプト:FaceDetectionをつけています。

Unity側は大体これだけです!

Androidプロジェクトの作成

Android Studioで新規プロジェクトを作成します。
パッケージ名は
com.example.opencvfacedetection
とします。

プロジェクトの設定/OpenCVモジュールのインポート

UnityにインポートするAARファイルを作成するために新しくModuleを作成します。
プロジェクトを右クリック>New>Module>Android Library
名前はlibとします。
パッケージ名は
com.example.opencvfacedetection.lib
となるように修正します。

次に、AndroidでOpenCVを使う準備をします。
OpenCV4Android SDKをダウンロードページから取得します。
現時点での最新の3.4.1を取得しました。
参考:Android OpenCV library

zipを解凍して顔検出用の学習済みデータ、
OpenCV-android-sdk\samples\face-detection\res\raw\lbpcascade_frontalface.xml
をlibモジュール中の
res\raw\以下にコピーします。(rawフォルダはなければ作成します。)

次にFile>New>Import Moduleで
OpenCV-android-sdk\sdk\java
を選択します。
すると
OpenCVLibrary341
というモジュールが追加されます。
OpenCVLibrary341モジュールのbuild.gradleで
compileSdkVersion 14
というのがエラーになっていたのでメインのプロジェクトと同じ24を指定します。

モジュールのインポートが完了したら
File>Project Structure>(左ペイン)Modules>(メインプロジェクト名)>Dependenciesタブ>+ボタン
をクリックして、OpenCVLibrary341モジュールを選択します。

これでやっとAndroidでOpenCVを使う準備が整いました!
これ以降はJavaからOpenCVのクラスを呼ぶことができます。

顔認識Activityの開発

新しくUnityPlayerActivityを継承したActivityを作成します。
つまり冒頭で述べたUnityPlayerActivity Java コードの拡張の方法です。

CustomMainActityというクラスを作成しました。
これがデフォルトのUnityPlayerActivityの代わりに、Unity起動時に呼ばれるようにします。
そのためにはUnityのAssets/Plugins/Android以下にAndroidManifest.xmlを配置します。
(詳細はGithub)

大まかに処理を説明すると、
CustomMainActity起動

OpenCVのクラスでカメラが起動

画像取得時のリスナーで顔認識

認識結果を描画した画像でテクスチャ用Bitmapオブジェクトを作成

OpenGLでテクスチャ作成
となります。

ソースはGithubに上げていますのでCloneしてご確認ください。

ビルド

Gradleメニューのassembleをクリックするとビルドが開始され、
うまくいけば、OpenCVFaceDetection\build\outputs\aar以下にAARファイルが出力されます。
(エラーになった場合Build>Clean Projectをしてからやり直してみてください)
また、OpenCVのARRもビルドします。
成功すると
OpenCVFaceDetection\openCVLibrary341\build\outputs\aar\openCVLibrary341-release.aar
が出力されます。

Unityにインポートしてビルド

出力された2つのAARファイル(lib-release.aar、openCVLibrary341-release.aar)
をUnityのAssets>Plugins>Android以下にコピーします。

また、OpenCV SDKの中に
OpenCV-android-sdk\sdk\native\libs\アーキテクチャ名
の下にOpenCVの機能をJavaから呼ぶために必要な.soファイルが入っています。
アーキテクチャはいろいろあるみたいですがひとまずここでは
armeabi-v7aとx86だけ対応することにします。
OpenCV-android-sdk\sdk\native\libs\armeabi-v7a\libopencv_java3.so

OpenCV-android-sdk\sdk\native\libs\x86\libopencv_java3.so
をAssets/Pulugins/Androidの下の同じアーキテクチャ名のフォルダにコピーします。

実行

動作するとこんな感じになります。
1280×720で実行しています。
端末はMoto g5という結構お安めの機種です。
それでもかなりサクサク動作しています。

ハマりどころ

Android+OpenGLを扱ったことがなかったのでテクスチャ生成に手間取りました。
ひとまずちゃんとテクスチャが生成されているかサンプルプログラムを動かしてみたり。

UnityからAndroidのアクティビティを呼ぶときのスレッドの違いが腹落ちできていません…。
createTexture()を読んだ時のポインタの値は同じなのにテクスチャが更新されなかったりといろいろハマり、
結果今の様になっています。

Unityでビルドするとエラーが出ました。

CommandInvokationFailure: Gradle build failed. 
C:/Program Files/Android/Android Studio/jre\bin\java.exe -classpath 
"H:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Tools\gradle\lib\gradle-launcher-4.0.1.jar"
 org.gradle.launcher.GradleMain "-Dorg.gradle.jvmargs=-Xmx2048m" "assembleDebug"

stderr[

FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring root project 'gradleOut'.
> Failed to find Build Tools revision 28.0.0

BuildSettingでBuild SystemをInternalにする。
or
こちらで解決。

おまけ: UnityでOpenCVを使う他の方法

今回の方法を使う前は、以下の構成をとっていました。

Unity
|- WebCamTextureを使ってカメラ画像を取得
|- カメラ画像のポインタをC++に渡す
C++
|- OpenCVで顔認識
Unity
|- 顔認識結果(画像の座標)をC++側から取得、表示

こちらの方が構成としては簡単です。
しかし、恐らくWebCamTextureから取得できる画像データのフォーマットをうまくcv::Matに変換できなかったせいだと思うのですが、
全然顔認識されませんでした。
Android OpenCV libraryに付属のサンプルではかなり高速&正確に顔認識されていたので、
Unityからその部分を拝借する構成にしました。

NEXT

先日こんなアプリをリリースしました。

エンタメ特化型ToDoアプリ [NEXT]
logo512


Android app on Google Play

レビューも頂きました!! 本当に有り難うございます!
Androidアプリ発見サイト -Appliv様
アンドロイダー様

自分自身でずっと欲しいと思っていたアプリです。
なので自分が使えればそれだけでも良いんですが、無駄にフォロー機能とか付けてます。
いや無駄ではないんですけどね。
良かったら使ってみてくださいませ。
たくさんの人に使ってもらえたらiOS版のリリースが早まります。はい。がんばります。

YouTubeの動画を目覚ましに設定できるアプリ VideoAlarmアップデートしました

去年、MovieAlarmというAndroidアプリをリリースしました。
これはYouTubeから自分の好きな動画を選んでアラームに設定できるって言うアプリです。

リリースしただけで半年ほど放置していたのですが、
この度、心機一転装いも新たにVideoAlarmという名前でアップデートしました。
(随分ブログに書くのが遅くなってしまいましたが…)
Android app on Google Play
好きなアーティストのPVなんか設定しておくと割りといい感じです。
無料なのでみなさん是非使ってみてくださいね^^

VideoViewとMediaController

最近更新頻度が落ちている。
一時期は「毎日更新しなきゃダメだよね。」とか言ってたのに。。。いかんいかん。

さて、本題だが、
今回はAndroidレイアウトの簡単なメモ。
VideoViewとそれを制御するMediaControllerについて。

とくに何も考えずにVideoViewを使うとMediaControllerが画面の最下部に表示されてしまう。

VideoViewの高さを指定したり、MediaControllerのマージンを設定したり、いろいろ試したが、
結局VideoViewとMediaControllerの間に出来る隙間は一体なんなのか解らずじまい。

もうMediaControllerは表示できる一番下に表示されるものだ、と割り切って、
VideoViewとMediaControllerをLinearLayoutで囲む事にした。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <LinearLayout
        android:id="@+id/linear_layout"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >

	    <TextView
	        android:layout_width="fill_parent"
	        android:layout_height="wrap_content"
	        android:text="@string/hello" />

	    <VideoView 
	        android:id="@+id/video_view"
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content" />

	    <MediaController 
	        android:id="@+id/controller"
	        android:layout_width="wrap_content"
	        android:layout_height="wrap_content"
	        android:visibility="visible" />

    </LinearLayout>

	<Button 
	android:id="@+id/setting"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:layout_alignParentBottom="true"
	android:text="@string/setting_btn" />

</RelativeLayout>

上手くいった。

ソーシャル ヒッチハイク

もう本日になってしまいましたが、いよいよABC 2012が開催されますね。
先日投稿した通り、今回、自分の初Androidアプリを出品させて頂いてます。
(Google Playの開発者登録って半日くらいかかるんですね。間に合わないかと思った(^_^;))
昨日やっとリリースしました。
これです。どうぞよろしくお願いします。

ソーシャル ヒッチハイク

実はこれ、某アプリコンテストように企画したものです。
(ま、コンテストの結果は鳴かず飛ばずでしたが…。)
ということもあり実装期間が1ヶ月くらいしかなかったのでアイデアを思いつき次第、直ぐに最低限の機能だけ実装する事にしました。
コンテストが終了してからまた1ヶ月くらいあったので細々した所を直して行きました。
初めてのAndroidアプリでしたが、このアプリではAndroid SDKの基本から、Androidの機能(GPSなど)とかFacebookやGoogle MapなどのAPI連携、DBサーバとの通信などなど色々な事を実際に試せたのが良かったかと。
勉強会などでいろいろな方にアドバイスいただきなんとか完成させる事が出来ました。
みなさん、本当に有り難うございました。

自分で作っておいて言うのもなんですが、今の時点ではあまり「使えるアプリ」とは言い難いかな、と思ってます。
こういうアプリは「1人でも使えてみんなで使うとより楽しい」って感じにしないとユーザ数が伸びないと思います。
現状では「1人でも使える」機能がありません。
それにこのアプリの需要が有るのか自体疑問でした。
なのでまだまだ至らない点が多いアプリですが取り敢えず公開してしまおう! ということで今回リリースしちゃったわけですw
需要があれば色々改善して行くので、何かご意見ご感想等有りましたらお気軽に。
このブログにコメントして頂くか、またはcontact☆design-ambience.com(☆は@に変えてください)までよろしくお願いしますm(_ _)m

というような背景もあり今のところマネタイズはあまり考えてません。
次はもっと多くのユーザに使って頂けるような企画をしてちゃんと練って、実装して、マネタイズまで行ければと思います。
次はAndroidのセンサー系とかマルチタッチなんかに挑戦したい。

今週末のイベント

今週末はABCがありますね。
注目の登壇者も多く、非常に楽しみ。

そして今回バザールの方に出品します。
詳細はまた後日。

初アプリ…大変です…(~_~;)

Viewの位置を動的に変える

AndroidのTextViewとかLinearLayoutで括ったViewをごそっと移動させる方法が解らなかったのでメモ。
Viewを移動させることでページ遷移に見せかけるというような使い方をしています。

要はJavaのコードの中でViewの位置を再定義しているだけなんですけどね(^_^;)
ボタンを押すとViewの位置が変わるだけという、凄くシンプルなサンプルを張っておきます。

主題と少し離れますが、LinearLayoutでは「android:orientation=”vertical”」という様にorientationを指定してやらないと意味不明な見た目になってしまいます。
これでかなりハマってしまいました(~_~;)

Java

package com.android.sample_app;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

public class SampleAppActivity extends Activity {

	Button moveBtn;
	LinearLayout layout;
	TextView helloTxt;
	TextView description;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // 要素の取得
        moveBtn = (Button) findViewById(R.id.move_btn);
        layout = (LinearLayout) findViewById(R.id.layout);

    }

    public void move(View v) {

    	int topMargin = 100;

    	layout.layout(0, topMargin, layout.getWidth(), topMargin + layout.getHeight());

    }
}

XML

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="400dip"
    android:layout_height="900dip"
    android:orientation="vertical" >

    <Button
        android:id="@+id/move_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/move_btn" 
        android:onClick="move"
    />

    <LinearLayout
        android:id="@+id/layout"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:background="#123456"
    >
	    <TextView
	        android:layout_width="fill_parent"
	        android:layout_height="wrap_content"
	        android:text="hello"
	    />

	    <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="hello"
        />
    </LinearLayout>

</LinearLayout>

そろそろマーケット対策を

先日『知名度ゼロ資本力なし それでも、あなたのiPhoneアプリでランキング1位を獲る80の秘訣』という本を購入しました。

自分が今取り組んでいるのはAndroidアプリですがiPhoneアプリのノウハウが使えるようです。
お世話になっている方からの猛烈なプッシュもあり購入しました。
心構えから対策方法まで具体的に書かれています。
冒頭ではアプリビジネスに取り組むメリットや成功した後の明るい未来について力強く語られており、半ば半信半疑(ごめんなさいw)ながらもやる気を引き起こされます。
しかし割りのいい話ばかりが書いているわけでもなく、時には結構シビアなことも書かれています。

80もの秘訣を全部やるのは大変そうですが、頑張って1つずつこなしていけば本当にランクインしそうな内容です^^
(ここではその内容は書けませんが。興味のある方は書店で笑)
結局、要はやってみる事が大事ですね。
今月末には初Androidアプリをリリースできそうなので大いに役立たせて頂こうかと思います。

ListViewをカスタマイズする(1)

ListViewの一行に3行のテキストを表示させたい。
完成イメージはこれ↓
————————-
Taro YAMADA
male
hoge
————————-

今回は『Androidプログラミングレシピ』を参考にさせてもらいます。
ちょっと脱線して書籍の簡単なレビューを書くと…、
この本は僕がAndroidに手を出して直ぐに購入した技術書です。
初心者には少し難しかったので購入当初は挫折して一旦他の技術書に助けを求めてしまいました笑
初心者にとっつきにくい理由の1つとしてはこの本がレシピ本だからだと思います。
この本はレシピとして、地図にマーカーを加えるとか画像にマスクをかけるといった様なアプリの要所要所で使えそうなテクニックが説明されています。
作りたいアプリがある程度固まって、この部分はどうやって作ろうか?となった時つかえそうなレシピが無いか探す、といった感じで使わせてもらっています。

さてさて、このレシピ本によりますと、、、

ListViewを使うにはListAdapterクラスを使う必要があり、
簡単なレイアウトであればArrayAdapterなどの既存のアダプターを使うだけでいい。
しかし、リスト行をカスタマイズするためにはアダプターを拡張する必要がある、とのこと。

実装に移る前に簡単に仕様をおさらいしておくと、

  • リストの各行には3つのテキストデータを表示する
  • テキストデータはDBから取得する
  • 更新ボタンがあり、クリックするとリストが更新される

以上です。
次回から実装して行きます。

タッチされた時の画像を変える

画像をボタンとして使用する時、タッチされたらへこんだ様な画像に差し替える事で、
押した感を表現したい。

用意するのは画像2枚(通常時、押された時)とxmlファイルだけで対応できる。
はじめてxmlファイルをボタンとして定義できると知ったときは不思議な感じだった。ちょっと感動した笑

通常時画像:normal.png
押された時画像:pressed.png
ボタン定義xml:botton.xml
を全部res/drawableに入れておけば良い。

botton.xmlの例はこんな感じ。

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_pressed="true"
        android:drawable="@+drawable/pussed">
    </item>
    <item
        android:drawable="@+drawable/normal">
    </item>

</selector>