OpenCV による飛跡の認識(1)

動画に対して、1フレームごとに以下のような処理を施します。

UIImage フォーマットの image を Mat フォーマットに変換した後、cvtColor で gray scale にします。

cv::Mat mat;
UIImageToMat( image, mat );
cv::Mat mat_gray = mat.clone();
cv::cvtColor( mat_gray, mat_gray, CV_BGR2GRAY );

次に、gray scale にした mat_gray を2値化します。

cv::Mat mat_bin = mat_gray.clone();
cv::cvtColor( mat_bin, mat_bin, threshold, 255, CV_THRESH_BINARY );
 

gray scale は、各ピクセルの値が0(黒)~255(白)の間の値をとっていますが、ある値を境界に黒(0)と白(255)の2値のみに変換します。ここでは、”threshold” でその境界値を与えています。

(左図) 霧箱画像を gray scale に変換したもの。(右図) 2値化に変換したもの。

上図は、x=650~960, y=360~480 あたりに横向きに走る白い直線状の飛跡と、大小様々な大きさのたくさんの白いドットが映っています。下図は、2値化の様子を詳しく見たものです。threshold は、バックグラウンドノイズ(白いドット)をできるだけ拾わないようにしながら、必要となる飛跡を拾うように選択します。

(左上) x=650~960, y=360~480 あたりにある、横向きの直線状の白い飛跡が、赤点線で囲われている。(左下) 左上の赤点線領域を y軸上に射影した1次元ヒストグラム。(右図) threshold = 100 で2値化した図。

上図からわかるように、飛跡を拾うように threshold を100まで下げると、多くのバックグラウンドノイズも合わせて拾ってしまっています。このバックグラウンドノイズが時間的に変化しないという特徴を持っていれば、各ピクセル値の時間平均を差し引くことで除くことが可能です。それに関しては後程説明します。

次に、2値化で白に選ばれた領域の境界点を探します。

cv::findContours( mat_bin, contours_raw, CV_RETR_LIST, CV_CHAIN_APPROX_NONE );

(左図) findContours で見つかった境界点を全て図示したもの。ここではわかりやすくするために各領域ごとに色を変えて表示しています。 (右図) 境界点のうち、点の数が80以上の領域のみを表示したもの。

上左図の例では、バックグラウンドノイズが多いため、findContours で見つかった境界点の数が膨大になってしまっています。境界点の数が80以上のもののみを図示したのが上右図で、欲しい飛跡が抽出されています。しかし、このような抽出方法では短い飛跡が拾えなくなってしまうため、いずれにせよ、バックグラウンドノイズを落とすことは重要になります。下図に、2本の飛跡をズームインしたものを載せます。

選択された飛跡のズームイン画像。緑と赤の点は、findContours で返された点の集合を表す。

最後に、直線によるフィッティングを行い、直線らしさの判定、フィットで得られた直線の長さ等を得ます。

for( int i = 0; i < contours_raw.size(); i ++ ){
  cv::Vec4f line;
  cv::fitLine( contours_raw[i], line, CV_DIST_L2, 0, 0.01, 0.01 );
}

下図に、フィットで得られた直線をシアン色で合わせて表示します。

フィットで得られた直線をシアン色で表示。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です