5

どの円が塗りつぶされているかを把握するためにopencvの問題に取り組んでいます。ただし、円のエッジが誤検知の原因になる場合があります。RGB の R 値が高いピクセルをすべて白にすることで、これらの円を消すことができるのではないかと考えています。私のアプローチは、ピンクがかったピクセルのマスクを作成し、元の画像からマスクを差し引いて円を削除することです。今のところ、私は黒いマスクを手に入れています。私は何か間違ったことをしています。ガイドしてください。

    rgb = cv2.imread(img, cv2.CV_LOAD_IMAGE_COLOR)
    rgb_filtered = cv2.inRange(rgb, (200, 0, 90), (255, 110, 255))
    cv2.imwrite('mask.png',rgb_filtered) 

ここに画像の説明を入力

4

3 に答える 3

5

これが私の解決策です。残念ながらC++にもあり、これがどのように機能するかです:

  1. 画像のしきい値を設定して、背景の部分を見つけます (ホワイト ペーパー)
  2. 輪郭を抽出して円を見つけます。
  3. 各輪郭は円であると想定されるため、その輪郭を囲む最小の円を計算します。入力に問題がなければ、パラメーターの調整は不要です (つまり、すべての円は 1 つの輪郭であるため、円は描画によって接続されない場合があります)。
  4. 各円について、前景 (描画) または背景 (ホワイト ペーパー) のピクセルが内側にあるかどうかを確認します (ある比率のしきい値によって)。

    int main()
    {
    cv::Mat colorImage = cv::imread("countFilledCircles.png");
    cv::Mat image = cv::imread("countFilledCircles.png", CV_LOAD_IMAGE_GRAYSCALE);
    
    // threshold the image!
    cv::Mat thresholded;
    cv::threshold(image,thresholded,0,255,CV_THRESH_BINARY_INV | CV_THRESH_OTSU);
    
    // save threshold image for demonstration:
    cv::imwrite("countFilledCircles_threshold.png", thresholded);
    
    
    // find outer-contours in the image these should be the circles!
    cv::Mat conts = thresholded.clone();
    std::vector<std::vector<cv::Point> > contours;
    std::vector<cv::Vec4i> hierarchy;
    
    cv::findContours(conts,contours,hierarchy, CV_RETR_EXTERNAL, CV_C    HAIN_APPROX_SIMPLE, cv::Point(0,0));
    
    
    
    // colors in which marked/unmarked circle outlines will be drawn:
    cv::Scalar colorMarked(0,255,0);
    cv::Scalar colorUnMarked(0,0,255);
    
    // each outer contour is assumed to be a circle
    // TODO: you could first find the mean radius of all assumed circles and try to find outlier (dirt etc in the image)
    for(unsigned int i=0; i<contours.size(); ++i)
    {
            cv::Point2f center;
            float radius;
            // find minimum circle enclosing the contour
            cv::minEnclosingCircle(contours[i],center,radius);
    
            bool marked = false;
    
        cv::Rect circleROI(center.x-radius, center.y-radius, center.x+radius, center.y+radius);
        //circleROI = circleROI & cv::Rect(0,0,image.cols, image.rows);
    
        // count pixel inside the circle
        float sumCirclePixel = 0;
        float sumCirclePixelMarked = 0;
        for(int j=circleROI.y; j<circleROI.y+circleROI.height; ++j)
            for(int i=circleROI.x; i<circleROI.x+circleROI.width; ++i)
            {
                cv::Point2f current(i,j);
                // test if pixel really inside the circle:
            if(cv::norm(current-center) < radius)
                {
                    // count total number of pixel in the circle
                    sumCirclePixel = sumCirclePixel+1.0f;
                    // and count all pixel in the circle which hold the segmentation threshold
                    if(thresholded.at<unsigned char>(j,i))
                        sumCirclePixelMarked = sumCirclePixelMarked + 1.0f;
                }
            }
    
        const float ratioThreshold = 0.5f;
        if(sumCirclePixel)
            if(sumCirclePixelMarked/sumCirclePixel > ratioThreshold) marked = true;
    
        // draw the circle for demonstration
        if(marked)
            cv::circle(colorImage,center,radius,colorMarked,1);
        else
            cv::circle(colorImage,center,radius,colorUnMarked,1);
        }
    
    
    cv::imshow("thres", thresholded);
    cv::imshow("colorImage", colorImage);
    cv::imwrite("countFilledCircles_output.png", colorImage);
    
    cv::waitKey(-1);
    }
    

私にこれらの結果を与える:

大津しきい値処理後:

ここに画像の説明を入力

最終的なイメージ:

ここに画像の説明を入力

于 2014-04-08T10:54:49.950 に答える
1

私はPythonで解決策を考え出そうとしました。基本的には次のプロセスです。

  • ノイズを減らすためのガウスぼかし。
  • 大津の敷居。
  • 親を持たない輪郭を見つけます。それらの輪郭は円である必要があります。
  • 各輪郭内の白と黒のピクセルの比率を確認します。

アプリケーションに合わせて白比率のしきい値を調整する必要がある場合があります。妥当な値と思われる 0.7 を使用しました。

import cv2
import numpy

# Read image and apply gaussian blur

img = cv2.imread("circles.png", cv2.CV_LOAD_IMAGE_GRAYSCALE)
img = cv2.GaussianBlur(img, (5, 5), 0)

# Apply OTSU thresholding and reverse it so the circles are in the foreground (white)

_, otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
otsu = cv2.bitwise_not(otsu).astype("uint8")

# Find contours that have no parent

contours, hierarchy = cv2.findContours(numpy.copy(otsu), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
parent_contours = [contours[idx] for idx, val in enumerate(hierarchy[0]) if val[3] == -1]

# Loop through all contours to check the ratio of white to black pixels inside each one

filled_circles_contours = list()

for contour in parent_contours:

    contour_mask = numpy.zeros(img.shape).astype("uint8")
    cv2.drawContours(contour_mask, [contour], -1, 1, thickness=-1)
    white_len_mask = len(cv2.findNonZero(contour_mask))
    white_len_thresholded = len(cv2.findNonZero(contour_mask * otsu))
    white_ratio = float(white_len_thresholded) / white_len_mask

    if white_ratio > 0.7:
        filled_circles_contours.append(contour)

# Show image with detected circles

cv2.drawContours(img, filled_circles_contours, -1, (0, 0, 0), thickness=2)
cv2.namedWindow("Result")
cv2.imshow("Result", img)
cv2.waitKey(0)

これは、上記のコードを画像に適用して得た結果です。

黒丸検出結果

于 2014-04-08T11:23:42.473 に答える
0

これが私がそれをした方法です:

  1. グレースケールに変換し、ガウスぼかしを適用してノイズを除去します
  2. otsu thresholding を適用します。フォアとバックグラウンドを分離するのは非常に良いことです。それについて読む必要があります。
  3. ハフ円変換を適用して円の候補を見つけます。悲しいことに、これにはかなりの調整が必要です。おそらく流域セグメンテーションがより良い代替手段です
  4. 候補円から ROI を抽出し、黒と白のピクセルの比率を見つけます。

これが私のサンプル結果です: ここに画像の説明を入力

元の画像に結果を描画すると、次のようになります。 ここに画像の説明を入力

サンプル コードは次のとおりです (C++ で申し訳ありません)。

void findFilledCircles( Mat& img ){
    Mat gray;
    cvtColor( img, gray, CV_BGR2GRAY );

    /* Apply some blurring to remove some noises */
    GaussianBlur( gray, gray, Size(5, 5), 1, 1);

    /* Otsu thresholding maximizes inter class variance, pretty good in separating background from foreground */
    threshold( gray, gray, 0.0, 255.0, CV_THRESH_OTSU );
    erode( gray, gray, Mat(), Point(-1, -1), 1 );

    /* Sadly, this is tuning heavy, adjust the params for Hough Circles */
    double dp       = 1.0;
    double min_dist = 15.0;
    double param1   = 40.0;
    double param2   = 10.0;
    int min_radius  = 15;
    int max_radius  = 22;

    /* Use hough circles to find the circles, maybe we could use watershed for segmentation instead(?) */
    vector<Vec3f> found_circles;
    HoughCircles( gray, found_circles, CV_HOUGH_GRADIENT, dp, min_dist, param1, param2, min_radius, max_radius );

    /* This is just to draw coloured circles on the 'originally' gray image */
    vector<Mat> out = { gray, gray, gray };
    Mat output;
    merge( out, output );

    float diameter  = max_radius * 2;
    float area      = diameter * diameter;

    Mat roi( max_radius, max_radius, CV_8UC3, Scalar(255, 255, 255) );
    for( Vec3f circ: found_circles ) {
    /* Basically we extract the region of the circles, and count the ratio of black pixels (0) and white pixels (255) */
        Mat( gray, Rect( circ[0] - max_radius, circ[1] - max_radius, diameter, diameter ) ).copyTo( roi );
        float filled_percentage = 1.0 - 1.0 * countNonZero( roi ) / area;

        /* If more than half is filled, then maybe it's filled */
        if( filled_percentage > 0.5 )
            circle( output, Point2f( circ[0], circ[1] ), max_radius, Scalar( 0, 0, 255), 3 );
        else
            circle( output, Point2f( circ[0], circ[1] ), max_radius, Scalar( 255, 255, 0), 3 );
    }


    namedWindow("");
    moveWindow("", 0, 0);
    imshow("", output );
    waitKey();
}
于 2014-04-08T09:58:38.050 に答える