2012-07-24 16 views
15

私は、写真から細菌コロニーなどの円形オブジェクトを数えるアプリケーションを開発しています。反復的適応的閾値処理と形状解析による円形オブジェクトのクラスタ検出

オブジェクトが一般的に背景とはかなり異なるという事実は簡単です。背景には緩やかなだけでなく、急激な強度変化を提示する

  1. はしかし、いくつかの困難がトリッキーな分析を行います。

  2. コンテナの端では、オブジェクトは円形ではなく楕円形になります。
  3. オブジェクトのエッジがむしろあいまいです。
  4. オブジェクトはクラスタ化されます。
  5. オブジェクトが
  6. (直径の計6Px)非常に小さくすることができ、最終的には、アルゴリズムは、画像解析の深い理解を持っていない人々によって(GUI経由)が使用されますので、パラメータは、直感的で非常に少数でなければなりません。

この問題は、科学文献で何度も述べられており、たとえば円形ハフ変換や流域アプローチを使用して解決されていますが、私は決して満足できませんでした。

説明されている簡単なアプローチの1つは、距離変換を使用してクラスター化されたオブジェクトを適応型しきい値化および分割(前にthis postで説明したように)でフォアグラウンドにすることです。

私はこの方法をうまく実装しましたが、突然の強度の変化には必ずしも対応できませんでした。また、より斬新なアプローチを出すように仲間から依頼されました。

したがって私は前景を抽出する新しい方法を探していました。

したがって、他のしきい値/ブロブ検出方法を調べました。 私はMSERを試しましたが、私の場合は非常に堅牢ではなく、かなり遅いことがわかりました。

私は最終的にはこれまでのところ、私に優れた結果が得られ、アルゴリズムで出てきた:

  1. 私は私のイメージの3つのチャンネルを分割し、そのノイズ(ぼかし/ぼかし中央値)を削減。各チャネルについて:
  2. 元のチャネルと畳み込まれた(大きなカーネルのぼかしによって)差の絶対差を計算することによって、適応閾値処理の第1ステップの手動実装を適用する。その後、しきい値に関連するすべての値のために:
  3. 私は2の結果にしきい値を適用)
  4. 輪郭
  5. に検証を見つけたり、その形状(大きさ、面積、凸部の補助金に輪郭を無効に...
  6. 有効な連続領域(、すなわち等高線で区切られた)のみがアキュムレータ(チャネルごとに1つのアキュムレータ)に再描画されます。
  7. スレッショルドの値を超えて連続領域を累積した後、私は "スコアの領域"のマップで最終的に終了します。最も高い強度を有する領域は、形態学的フィルタ基準を満たす領域が最も頻繁に基準となる。
  8. 3つのマップ(チャネルあたり1)を

ちょうどあなたに私が仕事をしなければならない画像の種類を示すために、(しきい値がユーザによって制御されている)グレースケールをに変換し、しきい値処理されています enter image description here この写真は、上の3つのサンプル画像の一部と、下の各部分のアルゴリズム(青=前景)の結果を表しています。ここで

は私のC++実装です:そのようフォアグラウンド抽出アプローチの名前は何

  1. 、あなたがのために何らかの理由を参照してください:3-7

    /* 
    * cv::Mat dst[3] is the result of the absolute difference between original and convolved channel. 
    * MCF(std::vector<cv::Point>, int, int) is a filter function that returns an positive int only if the input contour is valid. 
    */ 
    
    /* Allocate 3 matrices (1 per channel)*/ 
    cv::Mat accu[3]; 
    
    /* We define the maximal threshold to be tried as half of the absolute maximal value in each channel*/ 
    int maxBGR[3]; 
    for(unsigned int i=0; i<3;i++){ 
        double min, max; 
        cv::minMaxLoc(dst[i],&min,&max); 
        maxBGR[i] = max/2; 
        /* In addition, we fill accumulators by zeros*/ 
        accu[i]=cv::Mat(compos[0].rows,compos[0].cols,CV_8U,cv::Scalar(0)); 
    } 
    /* This loops are intended to be multithreaded using 
    #pragma omp parallel for collapse(2) schedule(dynamic) 
    For each channel */ 
    for(unsigned int i=0; i<3;i++){ 
        /* For each value of threshold (m_step can be > 1 in order to save time)*/ 
        for(int j=0;j<maxBGR[i] ;j += m_step){ 
          /* Temporary matrix*/ 
          cv::Mat tmp; 
          std::vector<std::vector<cv::Point> > contours; 
          /* Thresholds dst by j*/ 
          cv::threshold(dst[i],tmp, j, 255, cv::THRESH_BINARY); 
          /* Finds continous regions*/ 
          cv::findContours(tmp, contours, CV_RETR_LIST, CV_CHAIN_APPROX_TC89_L1); 
          if(contours.size() > 0){ 
           /* Tests each contours*/ 
           for(unsigned int k=0;k<contours.size();k++){ 
            int valid = MCF(contours[k],m_minRad,m_maxRad); 
            if(valid>0){ 
             /* I found that redrawing was very much faster if the given contour was copied in a smaller container. 
             * I do not really understand why though. For instance, 
             cv::drawContours(miniTmp,contours,k,cv::Scalar(1),-1,8,cv::noArray(), INT_MAX, cv::Point(-rect.x,-rect.y)); 
             is slower especially if contours is very long. 
             */ 
             std::vector<std::vector<cv::Point> > tpv(1); 
             std::copy(contours.begin()+k, contours.begin()+k+1, tpv.begin()); 
             /* We make a Roi here*/ 
             cv::Rect rect = cv::boundingRect(tpv[0]); 
             cv::Mat miniTmp(rect.height,rect.width,CV_8U,cv::Scalar(0)); 
             cv::drawContours(miniTmp,tpv,0,cv::Scalar(1),-1,8,cv::noArray(), INT_MAX, cv::Point(-rect.x,-rect.y)); 
             accu[i](rect) = miniTmp + accu[i](rect); 
            } 
           } 
          } 
         } 
        } 
    /* Make the global scoreMap*/ 
    cv::merge(accu,3,scoreMap); 
    /* Conditional noise removal*/ 
    if(m_minRad>2) 
        cv::medianBlur(scoreMap,scoreMap,3); 
    cvtColor(scoreMap,scoreMap,CV_BGR2GRAY); 
    

    私は2つの質問がありますこの場合、それを使用することは不適切かもしれませんか?

  2. 輪郭を再帰的に見つけて描画することはかなり集中的なので、アルゴリズムを高速化したいと考えています。この目標を達成する方法を教えてください。あなたは助けるために

は、どうもありがとうございました

+2

あなたの代わりに、ステップ2と3の(http://en.wikipedia.org/wiki/Otsu%27s_method)[大津の方法]をしようとしました?これは、ヒストグラムから簡単に計算できる非常に高速な適応しきい値です。これは通常、背景抑制のために非常にうまく動作します。構文は例えば'cvThreshold(img_src、img_dest、128、255、CV_THRESH_BINARY | CV_THRESH_OTSU); '。 – smocking

+0

@smockingありがとう、私はしましたが、1)すべてのサンプルに対して適応閾値と同様に機能しないことがわかりました。そして2)、ステップ2および3の生成物は、後で必要とされるグレースケール画像を生成するために、比較的高速である(3-7と比較して)... –

+0

OK音が良い;連続した形を見つけるために[connected component analysis](http://en.wikipedia.org/wiki/Connected_component_labeling)を使うのはどうですか?残念ながら、それは基本OpenCVライブラリにはありませんが、それを行う[cvBlobsLib](http://opencv.willowgarage.com/wiki/cvBlobsLib/)というライブラリがあります。 – smocking

答えて

2

数年前、私は顕微鏡画像で細胞を検出aplicationを書きました。コードはMatlabで書かれています。私はこれが(今の私の最初のCVプロジェクトでした)よりも複雑であると思いますので、実際に役立つトリックの概要を説明します。 Btw、それは致命的に遅かったが、それは双子細胞の大きなグループを分離することに本当に良かった。

Iは、与えられた点は、セルの中心である可能性を評価することにより、メトリック定義: - テクスチャの輝度の分散が所定のパターン に続く - - 輝度がそれ 周りに円形パターンで減少しセルは隣接セルの%以上をカバーしません

それを使って、私は繰り返し、最良のセルを見つけ出し、見つかったものとしてマークし、次に次のセルを探し始めました。このような検索は高価なので、私は遺伝子アルゴリズムを使用して自分の特徴空間内をより早く検索しました。

一部の結果を以下に示す:

Cells 2 Cells

+0

ありがとう、私はあなたのプロジェクトに使用したアプローチが好きです!あなたが置いた写真のオリジナルを投稿できますか?私の場合、私は光度のパターンやテクスチャ(例えば、エッジの中のあるオブジェクトがより暗くなっている)についての余分な仮定をしたくないので、深く進むことはできないと思います。私は多目的な解決策を見つけることを目指しています。私が私の記事で述べたように、私は処理速度も心配しています。 –

+0

ええ、それは過度に複雑だと思いますが、結果は素晴らしかったです。私はより単純な解決策は、形態学のような良い結果を、適応型閾値化と組み合わせて与えると思います。私は写真を投稿し、後で、私が家に帰るときには、もっと詳しい情報があります。 – Sam