WallStudio

技術ブログや創作ブログに届かない雑記です

喋る同人誌が作りたい!② OpenCV編

©Google

前回,Android OSのAPIandroid.camera2)を直接利用することでカメラを使っていましたが,後の画像処理を見据えてOpenCVを使ったカメラ利用の実装にしていきたいと思います.

前回

wallstudio.hateblo.jp

…とその前に,前回スルーしていたエラーを消しておきます.

Gradleのバージョンアップ(3.x→4.4)で治りました.

Error:Execution failed for task ':app:preDebugAndroidTestBuild'. > Conflict with dependency 'com.android.support:support-annotations' in project ':app'. Resolved versions for app (26.1.0) and test app (27.1.1) differ. See https://d.android.com/r/tools/test-apk-dependency-conflicts.html for details.

ざっくり訳すと,「アプリのバージョン(26.1.0)とテストのバージョン(27.1.1)が違うので直せ」って感じ.アプリのバージョンはプロジェクト作成時に選ぶKitKatとかMarshmallowとかのあれですが,テストのバージョンなんていじってないぞ…(^ω^;)新しいプロジェクトを作っただけで出てくるのでいかがなものかとも思いますが.(環境の問題?)テストのエラーなのでapkは作れます.が,気持ち悪い&後々テストコード書くときに困るので直します.

最も一般的そうなソリューションがコレ.build.gradleにandroidTestCompile 'com.android.support:support-annotations:26.1.0'と書く. increment.hatenablog.com

でも何故かGradleが認識するテストのバージョンが変わりません.更に調べていると,依存しているモジュール内に27がある的な情報が…build.gradleに次のように書きます.

androidTestCompile 'com.android.support:support-annotations:27.1.1' compile 'com.android.support.test:runner:1.+'

nuruyu.hatenablog.com

これ通りにやれば解決したのですが,testが27,appが26って言われてるのに,androidTestCompile 'com.android.support:support-annotations:27.1.1'を書いて通る意味が分からない…あとrunner:1.+で明示を避けるのも釈然としません.そして更に他のソリューションが,

www.youtube.com 要約するとbuild.gradleの

androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

を次のように修正する

androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'

明示的にテストのバージョンを落としているのでさっきのよりはまだマシな気がします.(結局中身についてはイマイチ理解できていませんが…)

それでは本命のOpenCVをインポートしていきます.

AndroidOpenCVは基本的にCで書かれたネイティブなライブラリをJavaでラップしたものです.(もちろん自分でラッパーを実装する方法もあるけど超面倒くさい)ですので,1)AndroidStudioにJavaのライブラリをインポート.2)ネイティブのライブラリをインストール.という二つの手順が必要です.具体的な操作は次のようにやればOKですが,この記事少し間違っているみたいで,2の手順はネイティブライブラリをapkに埋め込む場合の方法です.この埋め込みタイプは公式でも非推奨ですが,個人的には多少の非合理性よりもユーザーの操作数を減らすことの方が重要だと考えているので,このままいきます.(なので,OpenCV Managerをユーザーがインストールする必要はありません)しかし,またもや私の環境ではエラーが出ました.

https://qiita.com/kodai100/items/6c9e8a34d0714913c017qiita.com

Error:Execution failed for task ':app:transformNativeLibsWithStripDebugSymbolForDebug'. A problem occurred starting process 'command 'C:\Users\huser\AppData\Local\Android\Sdk\ndk-bundle\toolchains\mips64el-linux-android-4.9\prebuilt\windows-x86_64\bin\mips64el-linux-android-strip''

commandが何を意味しているのかよくわかりませんが,とにかくmipsのライブラリが原因のようです.app/src/main/jniLibs/内のmipsとmips64を消します.するとエラーは消えました.(どうせスマホのCPUなんてほとんどARM,ほんのりx86でしょ)

確認のためにapkの中身を見てみましょう.apkは実質zipなので拡張子をzipに変更すれば解凍できます.

f:id:yukawallstudio:20180521030114p:plain

おったー!

取り敢えずこれがSUCCESEになることを確認したら,めでたくOpenCVのセットアップ完了です.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if(!OpenCVLoader.initDebug()){
        Log.i("OpenCV", "Failed");
    }else{
        Log.i("OpenCV", "successfully built !");
    }
}

OpenCVAPIからカメラを使います.

前回,AndroidOSのAPIandroid.camera2)を叩いた時はかなりコードが長く複雑になりましたが,その点OpenCVはレガシーなAPIのラッパーであることもあって(android.camera)かなりシンプルです.でも,公式でも非推奨になったAPIを使ってるので不安ですよね…いっそこれを参考にcamera2のラッパーに改造して使った方がいいのかもしれませんが,めんどくさい.

public class MainActivity extends Activity
        implements CameraBridgeViewBase.CvCameraViewListener{

    private CameraBridgeViewBase mCameraView;

    private BaseLoaderCallback mLoaderCallback
            = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            if (status == LoaderCallbackInterface.SUCCESS)
                mCameraView.enableView();
            else
                super.onManagerConnected(status);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mCameraView =findViewById(R.id.camera_view);
        mCameraView.setCvCameraViewListener(this);
    }

    static {
        // ネイティブライブラリ読み込み
        System.loadLibrary("opencv_java3");
    }

    @Override
    protected void onResume() {
        super.onResume();

        mLoaderCallback.onManagerConnected
                (LoaderCallbackInterface.SUCCESS);

        // OpenCV Managerつかうならこっち
        // OpenCVLoader.initDebug()で切り分けるのがお行儀良い
        //
        //OpenCVLoader.initAsync(
        //        OpenCVLoader.OPENCV_VERSION_3_0_0,
        //        this, mLoaderCallback);
    }

    @Override
    public void onCameraViewStarted(int width, int height) {
        Log.d("CvCamera","Start");
    }
    @Override
    public void onCameraViewStopped() {
    }

    @Override
    public Mat onCameraFrame(Mat inputFrame) {
        return inputFrame; // 30fps固定らしい
    }

}

参考

https://qiita.com/denjin-m/items/8b2f30b98ef4529b8f1fqiita.com

mimuuow.hatenablog.com

実行結果

けだまきちゃんとゆかりさんの可愛さに心に染み渡ります.また最近,A5の同人誌(B5は専用ケースx2に収納)やVOICEROIDを収めるために技術書が追いやられて床にスタックされていますw

向きやスケールが滅茶苦茶ですが,取り敢えずCVのカメラで表示することができました.これをうまく合わせるにはやっぱりMat→BitmapにしてImageViewにはっ付けるしかないのでしょうか?それなら前回のcamera2を使ったものの方がいいような気さえします.具体的な画像処理はonCameraFrameの引数であるMat(画素行列)をCVの関数でエイヤッ↑↑として返り値に渡せばOKです.OpenCV自体はそれなりに使った経験があるので一山超えた気分ですね.

全然関係ないですが,先日VOCALOIDのV4 flowerさん(花ちゃんさん)をお迎えいたしまして,VOCALOIDマスターデビューしました!(ドンドンパフパフ
尚,私は中学時代リコーダーも碌に吹けなかった以来ほぼ楽器に触れていないどころかカラオケすら何年も行っていないのでお察しです…(´;ω;`)でもこれで堂々と花ちゃんさんを愛で愛でできるようになりました!HIPHOPが好きなのでいつの日にか花ちゃんさんとBro.Hiみたいなラップができるように頑張っていきたいと思います.

めちゃすこ花ちゃんさん歌