直線を検出する(古典的Hough変換)その1

OpenCv android版での実験を始めてから1ヶ月経ちました。nooj loonさんの ぎーくなぁどを参考に進めて来ましたが、追いついてしまいました。OpenCvのandroid版に興味を持って何か作ってみようと思ったとき あまりの資料の無さにがっかりしていた中 大変お世話になりました。
今までは OpenCv-CookBook のテーマを片っ端から実験してきましたが これからは 面白そう そして ひょっとしたらタヌキがひそんでいそう と感じるテーマを選んで実験していくことにします。

まず第一弾は古典的Hough変換による 直線の検出です。
Hough変換による画像からの直線や円の検出参照)

OpenCvで Hough変換で直線の検出を行うには HoughLinesメソッドを使います。
static void HoughLines(Mat image, Mat lines, double rho, double theta, int threshold)
static void HoughLines(Mat image, Mat lines, double rho, double theta, int threshold, double srn)
static void HoughLines(Mat image, Mat lines, double rho, double theta, int threshold, double srn, double stn)
Mat image : 8ビット,シングルチャンネルの2値入力画像。
 この画像は関数により書き換えられる可能性があります。
Mat lines : 検出された直線が出力されるベクトル。各直線は,2要素のベクトル(R,θ) で表現されます。
 Rは原点(画像の左上コーナー)からの距離
 θはラジアン単位で表される直線の回転角度(0 垂直線,π/2 水平線)です.
double rho : ピクセル単位で表される距離分解能。
double theta : ラジアン単位で表される角度分解能。
int threshold : 閾値パラメータ.十分なヒット数(> threshold)を得た直線のみが出力されます。
double srn : マルチスケールハフ変換において,距離分解能 rho の除数となる値。
 粗い距離分解能は rho となり,細かい分解能は rho/srn となります。
 もし srn=0 かつ stn=0 の場合は,古典的ハフ変換が利用されます。
 そうでない場合は,両方のパラメータが正値である必要があります。
double stn : マルチスケールハフ変換において,角度分解能 theta の除数となる値。
上記の説明は 殆ど C++用OpenCv2.2リファレンス の内容ですが リファレンスにある 投票空間って何?? accumulator の日本語訳みたいだけど これって日本語??(笑)
マルチスケールハフ変換の説明を探してみましたが 見当たらないので,srn stn を省略した 古典的Hough変換を実験しましす。

 ケース1 一本の直線 

linesに入る結果を確認するため めちゃくちゃ単純な画像((50,250)-(200,50)を結んだ直線)を使って実験します。

1)linesには何が入る??
グレースケールに変換 → Canny変換でエッジ検出 → HoughLinesで直線検出 

Mat image = android.BitmapToMat(src2);
Mat edge = new Mat();
Mat lines = new Mat();
Imgproc.cvtColor(image , edge, Imgproc.COLOR_RGB2GRAY); 
Imgproc.Canny(edge, edge, 80, 100);
Imgproc.HoughLines(edge, lines, 1, Math.PI/180, 100);

で linesに何が入るか調べてみました。
lines={(189, 0.6457718), (192, 0.6457718)} でした。
正しいかどうかなんだかわかりません。求まった線を引いてみました。
(R,θ)が示す線は (R/cosθ,0)-(0,R/sinθ)であることから

double[] data;
double rho, theta;
Point pt1 = new Point();
Point pt2 = new Point();double x0, y0;
for (int i = 0; i < lines.cols(); i++){
    data = lines.get(0, i);
    rho = data[0];
    theta = data[1];
    double cosTheta = Math.cos(theta);
    double sinTheta = Math.sin(theta);
    pt1.x = rho / cosTheta;
    pt1.y = 0;
    pt2.x = 0;
    pt2.y = rho / sinTheta;

    Core.line(image, pt1, pt2, new Scalar(255, 0, 0), 1);
}

注)0 < θ < π/2 の場合のみ上記のコーディングでOKです。

上段 : 求まったlinesの値
中段 : 元画像に求まった線を引いた画像
下段 : Cannyで求めた輪郭

きれいに 白→黒 黒→白 の両端を拾っています。sobelを使えば一本になるのでしょうがすこしパラメータをいじってみます。

2)距離分解能を大きくしてみます。(rho=5)
 1)の結果2本線の距離は 192-189=3なので 単純発想で rho=5とすると 一本になるのでは?
  Imgproc.HoughLines(edge, lines, 5, Math.PI/180, 100);

あれれ 3本になっちゃった

3)閾値パラメータを大きくしてみます。(threshold =300)
 閾値パラメータで余分な線を消そうと
  Imgproc.HoughLines(edge, lines, 5, Math.PI/180, 300);
  確かにこのサンプルではうまくいっているように見えます。

 by Android2.2 with OpenCv 2.3.1 for Android  
スポンサーリンク
Rectangle大広告
Rectangle大広告

シェアする

  • このエントリーをはてなブックマークに追加

フォローする