null product's blog

Androidアプリ開発のノウハウ集

【Androidアプリ開発】CanvasでBitmap画像に図形を重ねて描画するサンプル

今回は、Canvasを使用してBitmap画像に図形を重ねて描画します。

下記記事で使用した原画像に、抽出した付箋紙の座標の枠線を描画していきます。

f:id:null-product:20170507204153p:plain

 

前準備

割愛します。(下記参照)

【Androidアプリ開発】OpenCVでBitmapから四角形を抽出するサンプル - null product's blog

 

ソースコード

サンプルソースコードは以下の通りです。 

 

なお、ソースコード内の以下の関数に関しては、下記の記事を参照下さい。

 ・onImageScan()

 ・getThreshold()

 ・getContour()

【Androidアプリ開発】OpenCVでBitmapから四角形を抽出するサンプル - null product's blog

 

説明

原画像から付箋紙の輪郭座標を抽出した後、以下の処理を行います。

 ①:描画先のVIEWを取得

 ②:VIEWに原画像(Bitmap)を登録

 ③:VIEWに抽出した輪郭座標を登録

 ④:VIEWの描画更新要求

 ⑤:背景色を設定

 ⑥:原画像(Bitmap)を描画

 ⑦:輪郭を描画

 

①:描画先のVIEWを取得

「findViewById()」を使用して、「activity_main.xml」に定義した独自VIEW(CanvasView)のオブジェクトを取得します。

 

②:VIEWに原画像(Bitmap)を登録

CanvasViewに作成した「setmBitmap()」を使用して、VIEWにBitmap画像を登録します。

ここで登録したBitmap画像を「⑥:原画像(Bitmap)を描画」の描画処理で描画します。

 

③:VIEWに抽出した輪郭座標を登録

CanvasViewに作成した「setmPoints()」を使用して、VIEWに枠線の頂点座標を登録します。

ここで登録した頂点座標を「⑦:輪郭を描画」の描画処理で描画します。

 

④:VIEWの描画更新要求

「invalidate()」でCanvasViewを再描画します。

この後、CanvasViewの「onDraw()」が実行され、以降の処理に続きます。

 

⑤:背景色を設定

まず、「drawColor()」を使用して背景色を設定します。

ここでは、黒地(Color.BLACK)を指定してます。

  

⑥:原画像(Bitmap)を描画

Bitmapの描画には、「drawBitmap()」を使用します。

その前に、「scale()」を使用して画面サイズに合わせた拡大率になるように調整してます。

 

⑦:輪郭を描画

枠線を描画するには、「moveTo()」「lineTo()」「drawPath()」を使用して、頂点4つをラインで繋げます。

これを枠線の数だけ繰り返します。

なお、「drawRect()」「drawCircle()」を使用して図形を重ねることも可能です。

 

 

以上で、Bitmapを背景に枠線の図形を描画することができます。

【Androidアプリ開発】OpenCVで画像から四角形を抽出するコツ

上記の記事では、Bitmap形式画像から四角形を認識し、その4点の座標数値を抽出する手法を説明しました。

今回の記事では、画像から四角形を抽出するコツを説明します。

今回のコツを取り入れることで、次の結果のように、綺麗に四角形を抽出できるようになります。

f:id:null-product:20170507203431p:plain

 

今回も、Androidアプリ【付箋カメラ】で使用している、実際の手法となります。

 

前準備

割愛します。(下記参照)

【Androidアプリ開発】OpenCVでBitmapから四角形を抽出するサンプル - null product's blog

 

ソースコード

割愛します。(下記参照)

【Androidアプリ開発】OpenCVでBitmapから四角形を抽出するサンプル - null product's blog

 

説明

今回は、ソースコード中の以下の関数の処理について説明します。

 ・getContour()

輪郭抽出の核となる関数です。

サンプルのソースコードでは、各チャネルに分離した二値画像と、最終的に合成した二値画像に対してこの関数を実行し、それぞれの輪郭情報を抽出していきます。

 

/* 二値画像中の輪郭を検出 */
List tmp_contours = new ArrayList();
Mat hierarchy = Mat.zeros(new Size(5, 5), CvType.CV_8UC1);
Imgproc.findContours(mat, tmp_contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_TC89_L1);

関数の引数となる二値画像データ(mat)から輪郭情報を抽出します。

輪郭の抽出には、OpenCVライブラリの「Imgproc.findContours()」関数を使用します。

 

if (Imgproc.contourArea(tmp_contours.get(i)) < mat.size().area() / (100 * 1)) {
    /* サイズが小さいエリアは無視 */
    continue;
}

輪郭のサイズが小さいエリアは無視することで、ノイズとなるデータを破棄します。

輪郭のサイズを取得するには、OpenCVライブラリの「Imgproc.contourArea()」関数を使用します。

上記の「100 * 1」は調整値なので、それぞれのアプリで変更すると良いでしょう。

上記を実施しない場合は、次の結果のようにノイズが残った状態となります。

f:id:null-product:20170507234707p:plain

 

/* 輪郭線の周囲長を取得 */
double arclen = Imgproc.arcLength(ptmat2, true);
/* 直線近似 */
Imgproc.approxPolyDP(ptmat2, approx, 0.02 * arclen, true);

 輪郭データの凸凹を無くす(頂点数を減らす)為に、OpenCVライブラリの「Imgproc.approxPolyDP()」関数を使用します。

また、近似精度として輪郭の周囲長を使用する為に、「Imgproc.arcLength()」関数を使用します。

なお、「0.02」は調整値なので、それぞれのアプリで変更すると良いでしょう。

 

if (approxf1.size().area() != 4) {
    /* 四角形以外は無視 */
    continue;
}

四角形(頂点数が4)以外のデータを無視するようにします。

上記を実施しない場合は、次の結果のように四角形以外のデータまで抽出してしまいます。

もし、四角形以外の輪郭を抽出したい場合は、「!= 4」部分を変更すると良いでしょう。

f:id:null-product:20170508001616p:plain

 

/* 輪郭情報を登録 */
contour.add(approxf1);

最後に、戻り値用の配列データに輪郭情報を登録します。

 

 

【Androidアプリ開発】OpenCVでBitmapから四角形を画像認識するサンプル

この記事では、Androidアプリ開発において、Bitmap形式画像から四角形を画像認識し、その4点の座標数値を抽出する手法を説明します。

今回はサンプルとして、次の画像から付箋紙4枚の座標を抽出します。

f:id:null-product:20170507122215p:plain

 

今回の手法はあくまで一例ですが、Androidアプリ【付箋カメラ】では、実際にこの手法により写真から付箋紙を抽出してます。

 

前準備

 Androidアプリで画像認識を行うにはOpenCVライブラリを使用すると便利です。

以下のサイトを参考にすると、簡単にOpenCVライブラリをインポートできました。

 OpenCV for AndroidをAndroid Studioに導入するメモ - Qiita

 

ソースコード

サンプルソースコードは以下の通りです。

gist.github.com

 

説明

処理としては、大きく以下の3段階となります。

 ①:画像読み込み

 ②:画像を二値化

 ③:輪郭の座標を取得

 

今回は、ソースコード中の以下の関数の処理について説明します。

 ・onImageScan()

 ・getThreshold()

 

輪郭抽出の核となる以下の関数に関しては、また別の記事で説明する予定です。

 ・getContour()

 

①:画像読み込み

Bitmap画像を読み込みます。

今回のサンプルでは、リソースから取得するようにしていますが、カメラやストレージ上から取得しても構いません。

Bitmap画像を読み込んだ後は、OpenCVで扱えるようにMat形式に変換します。

 

②:画像を二値化

画像を二値化します。

ただし、写真画像を普通に二値化すると、次の結果のようにノイズが載ってしまいます。

f:id:null-product:20170507200343p:plain

 

そこで今回の手法では、色空間をチャネル(RGBなど)に分離し、それぞれのチャネルに対して二値化を行っていきます。

ちなみに、使用するチャネルを絞ることで、特定の色のみを抽出することも可能となります。

 

②-1-1:RGB空間チャネルの取得

画像をRGB空間の3チャネルに分離します。

 

②-1-2:RGB空間 → グレースケール変換 → 二値化

RGB空間の3チャネルと、元の画像のグレースケールの差分を算出し、それぞれのチャネル値を強調します。

そして、その結果を二値化画像に変換します。

 

②-1-3:RGBの輪郭を取得

RGB空間の3チャネルの二値化画像から四角形の座標を抽出します。

 

②-2-1:YUV空間チャネルの取得
②-2-2:YUV空間 → グレースケール変換 → 二値化
②-2-3:YUVの輪郭を取得

RGB空間に実施した処理をYUV空間にも実施します。

必須ではありませんが、いろいろな色空間のチャネルを二値化することによって、輪郭抽出の精度を上げる効果がります。

他の色空間も使用すると、精度が上がる見込みですが、過剰認識(誤抽出)や処理の高負荷の原因にもなりますので、ご注意を。

 

②-3:マスク画像に輪郭を合成

各チャネルで抽出した座標データを使用し、抽出した四角形を黒地に白で塗り潰し、1つの二値画像に合成します。

この時点での二値化画像を表示すると、次のようになります。

f:id:null-product:20170507203431p:plain

 

「②:画像を二値化」で示した純粋な二値化と比べると、明らかに精度が上がってます。

 

③:輪郭の座標を取得

合成した画像から最終的な座標を抽出します。

抽出結果は、以下の通りです。4つの四角形の頂点座標(x, y)が抽出できています。

f:id:null-product:20170507202207p:plain

 

まとめ

今回は、OpenCVを使用してBitmapから四角形の4点座標を抽出する手法を取り上げました。

この4点座標データは、様々な処理に活用することができます。

例えば、Canvasを使って4点をラインで結ぶと次のようになります。

f:id:null-product:20170507204007p:plain

 

更に、原画像と合成させます。

f:id:null-product:20170507204153p:plain

 

原画像の付箋紙4枚の位置とピッタリ一致していることが分かります。

 

今後は、これらの表示方法やタッチ操作による頂点編集の手法などについても取り上げていきたいと思います。