OpenCv cookbookでは『楕円フィッティングを行う』となっていますが 手元のデータでは『楕円フィッティング』に適切なデータがなかったので 結果的に『輪郭を取得する』ことになりました。
輪郭の取得は findContoursメソッドで行います。
static void findContours(Mat image, java.util.List contours, Mat hierarchy,int mode, int method)
static void findContours(Mat image, java.util.List contours, Mat hierarchy, int mode, int method, Point offset)
Mat image : 入力画像,8ビット,シングルチャンネルの2値画像。
また,この関数は,輪郭抽出処理中に入力画像 image の中身を書き換えます。
java.util.List contours : 検出された輪郭.各輪郭は,点のベクトルとして格納されます。
Mat hierarchy : オプション.画像のトポロジーに関する情報を含む出力ベクトル。これは,輪郭数と同じ数の要素を持ちます。
各輪郭 contours[i] に対して,要素 hierarchy[i][0] , hiearchy[i][1] , hiearchy[i][2] , hiearchy[i][3] にはそれぞれ,同じ階層レベルに存在する前後の輪郭,最初の子輪郭,および親輪郭の contours インデックス(0 基準)がセットされます。また,輪郭 i において,前後,親,子の輪郭が存在しない場合,それに対応する hierarchy[i] の要素は,負の値になります。
int mode : 輪郭抽出モード
CV_RETR_EXTERNAL 最も外側の輪郭のみを抽出します。
すべての輪郭に対して hierarchy[i][2]=hierarchy[i][3]=-1 がセットされます。
CV_RETR_LIST すべての輪郭を抽出しますが,一切の階層構造を保持しません。
CV_RETR_CCOMP すべての輪郭を抽出し,それらを2階層構造として保存します。
上のレベルには,連結成分の外側の境界線が,下のレベルには,連結成分の内側に存在する穴の境界線が属します。ある連結成分の穴の内側に別の輪郭が存在する場合,その穴は上のレベルに属します。
CV_RETR_TREE すべての輪郭を抽出し,入れ子構造になった輪郭を完全に表現する階層構造を構成します。
int method : 輪郭の近似手法:
CV_CHAIN_APPROX_NONE すべての輪郭点を完全に格納します.つまり,この手法により格納された任意の隣り合う2点は,互いに8近傍に存在します。
CV_CHAIN_APPROX_SIMPLE 水平・垂直・斜めの線分を圧縮し,それらの端点のみを残します.例えば,まっすぐな矩形の輪郭線は,4つの点にエンコードされます。
CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS Teh-Chinチェーン近似アルゴリズムの1つを適用します. TehChin89 を参照してください.
Point offset : オプションのオフセット.各輪郭点はこの値の分だけシフトします.これは,ROIの中で抽出された輪郭を,画像全体に対して位置づけて解析する場合に役立ちます。
上記の説明は OpenCv C++リファレンスの説明ですが あまり良くわからない部分が多いです。
例によって まずは実験!!
実行結果 |
元データをグレースケール化した後 2値化し 輪郭を検出しました。
Mat gray = new Mat(); Mat dst_bin = new Mat(); Imgproc.cvtColor(image , gray, Imgproc.COLOR_RGB2GRAY); Imgproc.threshold(gray,dst_bin, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU); Mat hierarchy = new Mat(); List contours =new ArrayList(100); Imgproc.findContours(dst_bin, contours, hierarchy, Imgproc.CV_RETR_LIST, Imgproc.CV_CHAIN_APPROX_NONE); Imgproc.cvtColor(gray , gray, Imgproc.COLOR_GRAY2BGRA,4); fncDrawContours(gray, contours);
ところで 求めた輪郭の表示はc++版Documentでは drawContoursというメソッドで表示するサンプルもありましたが、見つけられなかったので 次の関数を作って表示しています。また見やすいようにグレースケール化した画像の上に輪郭を描画しました。
private void fncDrawContours(Mat image, List contours) { Point pt1 = new Point(); Point pt2 = new Point(); double data1[]; double data2[]; for ( int i=0; i < contours.size(); i++ ) { Mat m = contours.get(i); if (m.rows() > 80){ ←あまり小さな輪郭は除外 for (int j=0; j < m.rows()-1; j++){ data1 = m.get(j, 0); data2 = m.get(j+1, 0); pt1.x = data1[0]; pt1.y = data1[1]; pt2.x = data2[0]; pt2.y = data2[1]; Core.line(image, pt1, pt2, new Scalar(255, 0, 0), 1); } } } }
またまた OpenCvSharpの実験中にこの記事の誤りをみつけました。javadocに立派に drawContoursがあるではないですか!!! 下の修正内容を参照してください。結果
ちなみに元画像 および 2値化した画像は以下の通りです。
元画像
2値化した画像
修正
検索した輪郭を表示する方法として当初ごりごりと 自分でコーディングしましたが drawContoursというメソッドがありました。
static void drawContours(Mat image, java.util.List contours, int contourIdx, Scalar color)
static void drawContours(Mat image, java.util.List contours, int contourIdx, Scalar color, int thickness)
static void drawContours(Mat image, java.util.List contours, int contourIdx, Scalar color, int thickness, int lineType)
static void drawContours(Mat image, java.util.List contours, int contourIdx, Scalar color, int thickness, int lineType, Mat hierarchy)
static void drawContours(Mat image, java.util.List contours, int contourIdx, Scalar color, int thickness, int lineType, Mat hierarchy, int maxLevel)
static void drawContours(Mat image, java.util.List contours, int contourIdx, Scalar color, int thickness, int lineType, Mat hierarchy, int maxLevel, Point offset)
初めのコーディングの
fncDrawContours(gray, contours); を
Imgproc.drawContours(gray, contours, -1, new Scalar(255, 0, 0), 1); とします。
ここで 第2引数に -1を指定すると全ての輪郭が描画されます。
結果は下図の通りです。ザワメキ感が強いのは 短な輪郭も全て表示されているせいです。
あきらめずjavadocは見るものですね・・
by Android2.2 with OpenCv 2.3.1 for Android |