web-dev-qa-db-ja.com

画像opencvでのオブジェクト領域の検出

現在、OpenCV、C++バージョンで利用可能なメソッドを使用して、医療機器画像のオブジェクト領域を検出しようとしています。画像の例を以下に示します。 1

私たちが従う手順は次のとおりです。

  • 画像をグレースケールに変換する
  • メディアンフィルターの適用
  • ソーベルフィルターを使用してエッジを見つける
  • しきい値25を使用して、結果をバイナリイメージに変換します。
  • 画像をスケルトン化して、エッジがきれいであることを確認します
  • X個の最大の接続コンポーネントを見つける

このアプローチは画像に対して完全に機能します1そしてここに結果があります:

1-result

  • 黄色の境界線は、検出された接続コンポーネントです。
  • 長方形は、接続されたコンポーネントの存在を強調するためのものです。
  • わかりやすい結果を得るために、他のコンポーネントの内部に完全にある接続コンポーネントを削除しただけなので、最終結果は次のようになります。

1-endResult

これまでのところ、すべてが順調でしたが、画像の別のサンプルは、以下に示す作業を複雑にしました。 2

オブジェクトの下に小さな薄緑色のタオルがあると、次の画像が表示されます。

2-result

以前に行ったように領域をフィルタリングした後、これを取得しました:

2-endresult

明らかに、それは私たちが必要とするものではありません。

2-ExpectedResult

見つかった最も近い接続されたコンポーネントをクラスター化することを考えています(どういうわけか!!)タオルの存在の影響を最小限に抑えることができますが、それが何かできるか、誰かが以前にこのようなことを試みたことがあるかどうかはまだわかりませんか?また、誰かがこの種の問題を克服するためのより良いアイデアを持っていますか?

前もって感謝します。

20
Maystro

これが私が試したものです。

画像では、背景はほとんど緑がかっており、背景の領域は前景の領域よりもかなり大きくなっています。したがって、画像のカラーヒストグラムを取得すると、緑がかったビンの値が高くなります。小さい値のビンがゼロに設定されるように、このヒストグラムにしきい値を設定します。この方法では、おそらく緑がかった(値が高い)ビンを保持し、他の色を破棄します。次に、このヒストグラムを逆投影します。逆投影では、画像のこれらの緑がかった領域が強調表示されます。

逆投影: backprojection

  • 次に、この逆投影のしきい値を設定します。これが背景になります。

背景(形態学的フィルタリング後): background

  • 背景を反転して前景にします。

前景(形態学的フィルタリング後): foreground

  • 次に、前景の輪郭を見つけます。

これは妥当なセグメンテーションを与えると思います。これをマスクとして使用すると、GrabCutのようなセグメンテーションを使用して境界を微調整できる可能性があります(これはまだ試していません)。

編集:私はGrabCutアプローチを試してみましたが、実際に境界が洗練されました。 GrabCutセグメンテーションのコードを追加しました。

等高線: contours

前景をマスクとして使用するGrabCutセグメンテーション: gc

ヒストグラム処理部分にOpenCV C APIを使用しています。

// load the color image
IplImage* im = cvLoadImage("bFly6.jpg");

// get the color histogram
IplImage* im32f = cvCreateImage(cvGetSize(im), IPL_DEPTH_32F, 3);
cvConvertScale(im, im32f);

int channels[] = {0, 1, 2};
int histSize[] = {32, 32, 32};
float rgbRange[] = {0, 256};
float* ranges[] = {rgbRange, rgbRange, rgbRange};

CvHistogram* hist = cvCreateHist(3, histSize, CV_HIST_ARRAY, ranges);
IplImage* b = cvCreateImage(cvGetSize(im32f), IPL_DEPTH_32F, 1);
IplImage* g = cvCreateImage(cvGetSize(im32f), IPL_DEPTH_32F, 1);
IplImage* r = cvCreateImage(cvGetSize(im32f), IPL_DEPTH_32F, 1);
IplImage* backproject32f = cvCreateImage(cvGetSize(im), IPL_DEPTH_32F, 1);
IplImage* backproject8u = cvCreateImage(cvGetSize(im), IPL_DEPTH_8U, 1);
IplImage* bw = cvCreateImage(cvGetSize(im), IPL_DEPTH_8U, 1);
IplConvKernel* kernel = cvCreateStructuringElementEx(3, 3, 1, 1, MORPH_ELLIPSE);

cvSplit(im32f, b, g, r, NULL);
IplImage* planes[] = {b, g, r};
cvCalcHist(planes, hist);

// find min and max values of histogram bins
float minval, maxval;
cvGetMinMaxHistValue(hist, &minval, &maxval);

// threshold the histogram. this sets the bin values that are below the threshold to zero
cvThreshHist(hist, maxval/32);

// backproject the thresholded histogram. backprojection should contain higher values for the
// background and lower values for the foreground
cvCalcBackProject(planes, backproject32f, hist);

// convert to 8u type
double min, max;
cvMinMaxLoc(backproject32f, &min, &max);
cvConvertScale(backproject32f, backproject8u, 255.0 / max);

// threshold backprojected image. this gives us the background
cvThreshold(backproject8u, bw, 10, 255, CV_THRESH_BINARY);

// some morphology on background
cvDilate(bw, bw, kernel, 1);
cvMorphologyEx(bw, bw, NULL, kernel, MORPH_CLOSE, 2);

// get the foreground
cvSubRS(bw, cvScalar(255, 255, 255), bw);
cvMorphologyEx(bw, bw, NULL, kernel, MORPH_OPEN, 2);
cvErode(bw, bw, kernel, 1);

// find contours of the foreground
//CvMemStorage* storage = cvCreateMemStorage(0);
//CvSeq* contours = 0;
//cvFindContours(bw, storage, &contours);
//cvDrawContours(im, contours, CV_RGB(255, 0, 0), CV_RGB(0, 0, 255), 1, 2);

// grabcut
Mat color(im);
Mat fg(bw);
Mat mask(bw->height, bw->width, CV_8U);

mask.setTo(GC_PR_BGD);
mask.setTo(GC_PR_FGD, fg);

Mat bgdModel, fgdModel;
grabCut(color, mask, Rect(), bgdModel, fgdModel, GC_INIT_WITH_MASK);

Mat gcfg = mask == GC_PR_FGD;

vector<vector<cv::Point>> contours;
vector<Vec4i> hierarchy;
findContours(gcfg, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
for(int idx = 0; idx < contours.size(); idx++)
{
    drawContours(color, contours, idx, Scalar(0, 0, 255), 2);
}

// cleanup ...

PDATE:以下に示すように、C++インターフェイスを使用して上記を実行できます。

const int channels[] = {0, 1, 2};
const int histSize[] = {32, 32, 32};
const float rgbRange[] = {0, 256};
const float* ranges[] = {rgbRange, rgbRange, rgbRange};

Mat hist;
Mat im32fc3, backpr32f, backpr8u, backprBw, kernel;

Mat im = imread("bFly6.jpg");

im.convertTo(im32fc3, CV_32FC3);
calcHist(&im32fc3, 1, channels, Mat(), hist, 3, histSize, ranges, true, false);
calcBackProject(&im32fc3, 1, channels, hist, backpr32f, ranges);

double minval, maxval;
minMaxIdx(backpr32f, &minval, &maxval);
threshold(backpr32f, backpr32f, maxval/32, 255, THRESH_TOZERO);
backpr32f.convertTo(backpr8u, CV_8U, 255.0/maxval);
threshold(backpr8u, backprBw, 10, 255, THRESH_BINARY);

kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));

dilate(backprBw, backprBw, kernel);
morphologyEx(backprBw, backprBw, MORPH_CLOSE, kernel, Point(-1, -1), 2);

backprBw = 255 - backprBw;

morphologyEx(backprBw, backprBw, MORPH_OPEN, kernel, Point(-1, -1), 2);
erode(backprBw, backprBw, kernel);

Mat mask(backpr8u.rows, backpr8u.cols, CV_8U);

mask.setTo(GC_PR_BGD);
mask.setTo(GC_PR_FGD, backprBw);

Mat bgdModel, fgdModel;
grabCut(im, mask, Rect(), bgdModel, fgdModel, GC_INIT_WITH_MASK);

Mat fg = mask == GC_PR_FGD;
22
dhanushka

いくつかのオプションを検討します。カメラは動かないというのが私の想定です。私は画像を使用したことも、コードを記述したこともないので、これはほとんど経験からのものです。

  • エッジを探すだけでなく、セグメンテーションアルゴリズムを使用して背景を分離してみてください。ガウス混合がこれを助けることができます。同じリージョン(ビデオなど)にまたがる一連の画像を指定すると、永続的なリージョンをキャンセルできます。すると、楽器などの新しいアイテムが飛び出します。接続されたコンポーネントをBLOBで使用できます。

    • セグメンテーションアルゴリズムを見て、条件を最適化してこれを機能させることができるかどうかを確認します。主要な項目の1つは、カメラが安定していることを確認すること、または前処理で自分で画像を安定させることです。
  • 関心点を使用して、多くの新しい素材が含まれる画像内の領域を特定することを検討します。背景が比較的プレーンであることを考えると、針などの小さなオブジェクトは一連の関心点を作成します。タオルははるかにまばらでなければなりません。おそらく、接続されたコンポーネントのフットプリントの上に検出された関心点をオーバーレイすると、しきい値を設定できる「密度」メトリックが得られます。連結されたコンポーネントのアイテムの領域に対する関心点の比率が大きい場合、それは興味深いオブジェクトです。

    • このノートでは、凸包を使用して検出したオブジェクトをプルーニングすることにより、接続されたコンポーネントのフットプリントをクリーンアップすることもできます。これは、コンポーネント領域を伸ばすタオルに影を落とす医療機器などの状況に役立ちます。これは推測ですが、注目点は、エッジだけでなく、より多くの情報を提供します。
  • 最後に、明確なオブジェクトが表示された安定した背景があることを前提として、Bag-of-Featuresを調べて、画像内の個々のオブジェクトをそれぞれ検出できるかどうかを確認します。これらの画像のオブジェクトに一貫したパターンがあるように見えるので、これは便利かもしれません。針、ガーゼ、はさみなどの画像の大きなデータベースを構築できます。OpenCVにあるBoFがそれらの候補を見つけます。結果を比較するために行っている他の操作と組み合わせることもできます。

  • -
5
msmith81886

また、最初のバージョンにアイデアを提案します。領域の幅と高さが画像の幅と高さの半分より大きい輪郭をスキップすることもできます。

//take the rect of the contours

Rect rect = Imgproc.boundingRect(contours.get(i));

if (rect.width < inputImageWidth / 2 && rect.height < inputImageHeight / 2)

//then continue to draw or use for next purposes.
0
Tejash L