#はじめに
AndroidStudioのデフォルトだけで完結するアプリ作りにも飽きてきたので、
OpenCVを使って画像をあーだこーだしてアプリの幅を広げてみる。
手始めにインストールして試しに使ってみる。
#環境
OpenCV 4.1.2
AndroidStudio 3.4.1
#インストール
こちらの記事を参考にさせていただきました。
特に支障なくインストールとインポートは完了。
#Mat形式で取り込む
前回の記事でデバイスの画像データをbitmapで取り込むところまでやりました。
このbitmapをOpenCVで扱うためのMat形式に変換します。
OpenCVのLibraryに含まれているandroid.packageのUtilsクラスに以下のメソッドが用意されています。
これを使ってbitmapをMatに変換する。
Mat形式に画像を変換することでOpenCVのLibraryを用いて画像を色々と編集することができるようになります。
/**
* Short form of the bitmapToMat(bmp, mat, unPremultiplyAlpha=false).
* @param bmp is a valid input Bitmap object of the type 'ARGB_8888' or 'RGB_565'.
* @param mat is a valid output Mat object, it will be reallocated if needed, so Mat may be empty.
*/
public static void bitmapToMat(Bitmap bmp, Mat mat) {
bitmapToMat(bmp, mat, false);
}
//bmp_img:取り込んだbitmap
//mat_img:あらかじめMat形式の変数を用意しておく
Utils.bitmapToMat(bmp_img,mat_img);
#Matを使ってみる
今回は試しにボタンを押したらカラー画像をグレースケールに変換するアプリを作って見ます。
どのメソッドを使うかというと、公式ドキュメントから地道に探します。
Open Source Computer Vision
それかOpenCVをインストールした状態でプロジェクト全体を[gray]とかで検索するとそれっぽいのが引っかかります。
今回はImageprocクラスの中に以下のようなメソッドが用意されていました。
public static void cvtColor(Mat src, Mat dst, int code) {
cvtColor_1(src.nativeObj, dst.nativeObj, code);
}
codeの引数には、どのような変換を行うのかの定数を入れてあげます。
imageproc.hpp
のファイルに変換方法一覧があるのでそちらを参照。
今回はカラー画像からグレースケールに変換したいのでCOLOR_RGB2GRAY
を利用する。
Utils.bitmapToMat(bmp_img,mat_img); //bmp_img:取り込んだbitmap,mat_img:Mat形式の変数を用意しておく
Mat mat_output = new Mat();
cvtColor(mat_img,mat_output,COLOR_RGB2GRAY);
これでmat_img
の画像がグレースケールに変換されmat_output
に入れられるはず。
最後にこのmat_output
を再度bitmapに変換し、ImageViewに出力する。
private Mat mat_output = new Mat();
public void onClick(View v) {
Utils.bitmapToMat(bmp_img,mat_img); //BitmapからMatへ変換
cvtColor(mat_img,mat_output,COLOR_RGB2GRAY); //GrayScaleへ変換
Utils.matToBitmap(mat_output,bmp_img); //Bitmapに戻す
img_picture.setImageBitmap(bmp_img); //ImageViewへ表示
}
#クラッシュ発生!
これで完成!と思いきや、ボタンを押した瞬間にクラッシュ発生。
デバッグしてみると、そもそもMatをnewしているところでクラッシュが発生している。
エラーメッセージを見てみると...
java.lang.UnsatisfiedLinkError: No implementation found for long org.opencv.core.Mat.n_Mat() (tried Java_org_opencv_core_Mat_n_1Mat and Java_org_opencv_core_Mat_n_1Mat__)
Mat()が見つかりません、みたいなことが書いてあります。
困ったときのStackoverflowを見てみたら同じ境遇の人が見つかりました。
No implementation found for long org.opencv.core.Mat.n_Mat() error Using OpenCV
記事の内容をまとめると
- OpenCVのLibraryを読み込む前にOnCreate()が呼ばれてしまっている
- OpenCVManagerとやらを使って非同期でOpenCVを初期化する
- OnCreate()前にBaseLoaderCallbackを実行し、この中でMat()のインスタンスを宣言してあげる
- OnResume()で毎回OpenCVが導入されているかをチェックする
さらに少し調べてわかったことは
- このOpenCVManagerはユーザーごとに別途インストールしてもらう。
- Libraryをアプリに含めないことによりアプリの容量が小さくなるとのこと。
- OpenCVManagerを起動するためにOpenCVLoader.initAsyncを実行する
- Initが完了したタイミングでBaseLoaderCallbackが呼ばれるので、その中でMatの生成を行う
やってみた
とりあえずStackOverflowに書いてあったソースコードをそのまま流用しました。
デバッグしてみたところ、
- OnResume()内でmLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS)が呼ばれる
- onManagerConnected内でMatのインスタンスが生成される
という流れが確認できました。
てっきりアプリ起動時にOpenCVManagerのインストールを要求されるかと思ったら何もありませんでした。
またOnResume()内のOpenCVLoader.initAsyncも呼ばれなかった。(初回起動時見逃した...?)
AndroidやOpenCVのバージョンにもよるかもしれないです。
結果
できました!
写真は台湾のかき氷屋さんの「IceMonstar」のキャラクター?です。
マンゴーかき氷がすごい美味しかったので台湾行った際はぜひ。
最後に
環境構築に少しだけ手間取りましたが、一応OpenCVが扱えるようになりました。
公式ドキュメントを見たところ膨大なライブラリが用意されているようなので、これから色々触っていきたいと思います。