7

私の作品は、ドットの配列を持つ画像 (図 1) に基づいており、最終的な結果は図 4 に示されています。私の作品を順を追って説明します。

図1 元画像

ここに画像の説明を入力

ステップ 1:パフォーマンスを向上させるために削除したいドットや「リング」など、すべてのオブジェクトのエッジを検出します。そして、エッジ検出の結果を図 2 に示します。Canny エッジ検出器を使用しましたが、薄い灰色のドットではうまく機能しませんでした。私の最初の質問は、ドットの輪郭を閉じて、他のノイズをできるだけ減らす方法ですか?

図2 エッジ検出

ここに画像の説明を入力

ステップ 2:すべてのオブジェクトを拡張します。穴を埋める良い方法が見つからなかったので、直接拡張します。図 3 に示すように、穴が大きくなりすぎているように見え、他のノイズも大きくなっています。私の2番目の質問は、穴を同じ/類似のサイズで塗りつぶすために穴を埋めるまたは拡張する方法ですか?

図3 膨張

ここに画像の説明を入力

ステップ 3:すべてのドットの重心を見つけて描画します。図 4 に示すように、粗い画像処理により、「リング」のマークが存在し、一部のドットが 2 つの白いピクセルで表示されます。必要な結果は、ドットと 1 つのドットに対して 1 つの白いピクセルのみを表示する必要があります。

図 4: 重心

ここに画像の説明を入力

これらの3つのステップのコードは次のとおりです。誰かが私の仕事をより良くするのを助けることができますか?

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
#include <cv.h>
#include <highgui.h>
using namespace std;
using namespace cv;

// Global variables
Mat src, edge, dilation;
int dilation_size = 2;

// Function header
void thresh_callback(int, void*);

int main(int argc, char* argv)
{
    IplImage* img = cvLoadImage("c:\\dot1.bmp", 0);         // dot1.bmp = Fig. 1

    // Perform canny edge detection
    cvCanny(img, img, 33, 100, 3);

    // IplImage to Mat
    Mat imgMat(img);
    src = img;

    namedWindow("Step 1: Edge", CV_WINDOW_AUTOSIZE);
    imshow("Step 1: Edge", src);

    // Apply the dilation operation
    Mat element = getStructuringElement(2, Size(2 * dilation_size + 1, 2 * dilation_size + 1), 
                  Point(dilation_size, dilation_size));     // dilation_type = MORPH_ELLIPSE
    dilate(src, dilation, element);
    // imwrite("c:\\dot1_dilate.bmp", dilation);            

    namedWindow("Step 2: Dilation", CV_WINDOW_AUTOSIZE);
    imshow("Step 2: Dilation", dilation);

    thresh_callback( 0, 0 );

    waitKey(0);
    return 0;
}

/* function thresh_callback */
void thresh_callback(int, void*)
{
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;

    // Find contours
    findContours(dilation, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

    // Get the moments
    vector<Moments> mu(contours.size());
    for(int i = 0; i < contours.size(); i++) {
        mu[i] = moments(contours[i], false);
    }

    // Get the mass centers
    vector<Point2f> mc(contours.size());
    for(int i = 0; i < contours.size(); i++) {
        mc[i] = Point2f(mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00); 
    }

    // Draw mass centers
    Mat drawing = Mat::zeros(dilation.size(), CV_8UC1);
    for( int i = 0; i< contours.size(); i++ ) {
        Scalar color = Scalar(255, 255, 255);
        line(drawing, mc[i], mc[i], color, 1, 8, 0);
    }

    namedWindow("Step 3: Mass Centers", CV_WINDOW_AUTOSIZE);
    imshow("Step 3: Mass Centers", drawing);
}
4

2 に答える 2

10

結果を改善するためにできることがいくつかあります。画像のノイズを減らすために、キャニー オペレーターを適用する前にメディアン ブラーを適用できます。これは一般的なノイズ除去手法です。また、C API とIplImage.

    cv::Mat img = cv::imread("c:\\dot1.bmp", 0);         // dot1.bmp = Fig. 1

    cv::medianBlur(img, img, 7);

    // Perform canny edge detection
    cv::Canny(img, img, 33, 100);

これにより、エッジ イメージのノイズの量が大幅に減少します。 キャニー結果

ドットの元のサイズをより適切に保持するために、膨張ではなく、より小さなカーネルを使用してモルフォロジー クロージングを数回繰り返すことができます。これにより、ドットと円の結合も減少します。

// This replaces the call to dilate()
cv::morphologyEx(src, dilation, MORPH_CLOSE, cv::noArray(),cv::Point(-1,-1),2);

これは、 を使用して示されるように、3x3 カーネルで 2 回の反復を実行しますcv::noArray()

結果はよりきれいになり、ドットは完全に塗りつぶされます。

決算結果

パイプラインの残りの部分を変更せずにそのままにしておくと、最終的な結果が得られます。円からの偽の質量中心がまだいくつかありますが、元の方法よりもかなり少なくなっています。

質量センター

結果から円を完全に削除したい場合はcv::HoughCircles()、良い結果が得られるまでパラメーターを使用および調整してみてください。円全体が画像に表示されておらず、セグメントのみが表示されているため、これにはいくつかの問題があるかもしれませんが、試してみることをお勧めします. 最も内側の円を検出した場合は、それをマスクとして使用して、外部質量中心を除外できます。

于 2013-07-19T16:28:56.757 に答える