How can I filter out points of an edge

I am working on detecting the center and radius of a circular aperture that is illuminated by a laser beam. The algorithm will be fed images from a system that I have no physical control over (ie dimming the source or adjusting the laser position.) I need to do this with C++, and have chosen to use openCV.

In some regions the edge of the aperture is well defined, but in others it is very noisy. I currently am trying to isolate the "good" points to do a RANSAC fit, but I have taken other steps along the way. Below are two original images for reference:

testImage1testImage2

I first began by trying to do a Hough fit. I performed a median blur to remove the salt and pepper noise, then a Gaussian blur, and then fed the image to the HoughCircle function in openCV, with sliders controlling the Hough parameters 1 and 2 defined here. The results were disastrous: bad results

I then decided to try to process the image some more before sending it to the HoughCircle. I started with the original image, median blurred, Gaussian blurred, thresholded, dilated, did a Canny edge detection, and then fed the Canny image to the function.

I was eventually able to get a reasonable estimate of my circle, but it was about the 15th circle to show up when manually decreasing the Hough parameters. I manually drew the purple outline, with the green circles representing Hough outputs that were near my manual estimate. The below images are:

  • Canny output without dilation
  • Canny output with dilation
  • Hough output of the dilated Canny image drawn on the original image.
  • cannyimagetest

    As you can see, the number of invalid circles vastly outnumbers the correct circle, and I'm not quite sure how to isolate the good circles given that the Hough transform returns so many other invalid circles with parameters that are more strict.

    I currently have some code I implemented that works OK for all of the test images I was given, but the code is a convoluted mess with many tunable parameters that seems very fragile. The driving logic behind what I did was from noticing that regions of the aperture edges that were well-illuminated by the laser were relatively constant across several threshold levels (image shown below).

    image

    I did edge detection at two threshold levels and stored points that overlapped in both images. Currently there is also some inaccuracy with the result because the aperture edge does still shift slightly with the different threshold levels. I can post the very long code for this if necessary, but the pseudo-code behind it is:

    1. Perform a median blur, followed by a Gaussian blur. Kernels are 9x9.
    2. Threshold the image until 35% of the image is white. (~intensities > 30)
    3. Take the Canny edges of this thresholded image and store (Canny1)
    4. Take the original image, perform the same median and Gaussian blurs, but threshold with a 50% larger value, giving a smaller spot (~intensities > 45)
    5. Perform the "Closing" morphology operation to further erode the spot and remove any smaller contours.
    6. Perform another Canny to get the edges, and store this image (Canny2)
    7. Blur both the Canny images with a 7x7 Gaussian blur.
    8. Take the regions where the two Canny images overlap and say that these points are likely to be good points.
    9. Do a RANSAC circle fit with these points.
    

    I've noticed that there are regions of the edge detected circle that are pretty distinguishable by the human eye as being part of the best circle. Is there a way to isolate these regions for a RANSAC fit?

    Code for Hough:

    int houghParam1 = 100;
    int houghParam2 = 100;
    int dp = 10; //divided by 10 later
    int x=616;
    int y=444;
    int radius = 398;
    int iterations = 0;
    
    int main()
    {
    namedWindow("Circled Orig");
    namedWindow("Processed", 1);
    namedWindow("Circles");
    namedWindow("Parameters");
    namedWindow("Canny");
    createTrackbar("Param1", "Parameters", &houghParam1, 200);
    createTrackbar("Param2", "Parameters", &houghParam2, 200);
    createTrackbar("dp", "Parameters", &dp, 20);
    createTrackbar("x", "Parameters", &x, 1200);
    createTrackbar("y", "Parameters", &y, 1200);
    createTrackbar("radius", "Parameters", &radius, 900);
    createTrackbar("dilate #", "Parameters", &iterations, 20);
    std::string directory = "Secret";
    std::string suffix = ".pgm";
    Mat processedImage;
    Mat origImg;
    for (int fileCounter = 2; fileCounter < 3; fileCounter++) //1, 12
    {
        std::string numString = std::to_string(static_cast<long long>(fileCounter));
        std::string imageFile = directory + numString + suffix;
        testImage = imread(imageFile);
        Mat bwImage;
        cvtColor(testImage, bwImage, CV_BGR2GRAY);
        GaussianBlur(bwImage, processedImage, Size(9, 9), 9);
        threshold(processedImage, processedImage, 25, 255, THRESH_BINARY); //THRESH_OTSU
        int numberContours = -1;
        int iterations = 1;
        imshow("Processed", processedImage);
    }
    
    vector<Vec3f> circles;
    Mat element = getStructuringElement(MORPH_ELLIPSE, Size(5, 5));
    float dp2 = dp;
    while (true)
    {
        float dp2 = dp;
        Mat circleImage = processedImage.clone();
        origImg = testImage.clone();
        if (iterations > 0) dilate(circleImage, circleImage, element, Point(-1, -1), iterations);
        Mat cannyImage;
        Canny(circleImage, cannyImage, 100, 20);
        imshow("Canny", cannyImage);
        HoughCircles(circleImage, circles, HOUGH_GRADIENT, dp2/10, 5, houghParam1, houghParam2, 300, 5000);
        cvtColor(circleImage, circleImage, CV_GRAY2BGR);
        for (size_t i = 0; i < circles.size(); i++)
        {
            Scalar color = Scalar(0, 0, 255);
            Point center2(cvRound(circles[i][0]), cvRound(circles[i][1]));
            int radius2 = cvRound(circles[i][2]);
            if (abs(center2.x - x) < 10 && abs((center2.y - y) < 10) && abs(radius - radius2) < 20)  color = Scalar(0, 255, 0);
            circle(circleImage, center2, 3, color, -1, 8, 0);
            circle(circleImage, center2, radius2, color, 3, 8, 0);
            circle(origImg, center2, 3, color, -1, 8, 0);
            circle(origImg, center2, radius2,color, 3, 8, 0);
        }
        //Manual circles
        circle(circleImage, Point(x, y), 3, Scalar(128, 0, 128), -1, 8, 0);
        circle(circleImage, Point(x, y), radius, Scalar(128, 0, 128), 3, 8, 0);
        circle(origImg, Point(x, y), 3, Scalar(128, 0, 128), -1, 8, 0);
        circle(origImg, Point(x, y), radius, Scalar(128, 0, 128), 3, 8, 0);
        imshow("Circles", circleImage);
        imshow("Circled Orig", origImg);
        int x = waitKey(50);
    }
    Mat drawnImage;
    cvtColor(processedImage, drawnImage, CV_GRAY2BGR);
    return 1;
    }
    

    Thanks @jalconvolvon - this is an interesting problem. Here's my result: 在这里输入图像描述 What I find important on and on is using dynamic parameter adjustment when prototyping, thus I include the function I used to tune Canny detection. The code also uses this answer for the Ransac part.

    import cv2
    import numpy as np
    import auxcv as aux
    from skimage import measure, draw
    
    def empty_function(*arg):
        pass
    
    # tune canny edge detection. accept with pressing "C"
    def CannyTrackbar(img, win_name):
        trackbar_name = win_name + "Trackbar"
    
        cv2.namedWindow(win_name)
        cv2.resizeWindow(win_name, 500,100)
        cv2.createTrackbar("canny_th1", win_name, 0, 255, empty_function)
        cv2.createTrackbar("canny_th2", win_name, 0, 255, empty_function)
        cv2.createTrackbar("blur_size", win_name, 0, 255, empty_function)
        cv2.createTrackbar("blur_amp", win_name, 0, 255, empty_function)
    
        while True:
            trackbar_pos1 = cv2.getTrackbarPos("canny_th1", win_name)
            trackbar_pos2 = cv2.getTrackbarPos("canny_th2", win_name)
            trackbar_pos3 = cv2.getTrackbarPos("blur_size", win_name)
            trackbar_pos4 = cv2.getTrackbarPos("blur_amp", win_name)
            img_blurred = cv2.GaussianBlur(img.copy(), (trackbar_pos3 * 2 + 1, trackbar_pos3 * 2 + 1), trackbar_pos4)
            canny = cv2.Canny(img_blurred, trackbar_pos1, trackbar_pos2)
            cv2.imshow(win_name, canny)
    
            key = cv2.waitKey(1) & 0xFF
            if key == ord("c"):
                break
    
        cv2.destroyAllWindows()
        return canny
    
    img = cv2.imread("sphere.jpg")
    
    #resize for convenience
    img = cv2.resize(img, None, fx = 0.2, fy = 0.2)
    
    #closing
    kernel = np.ones((11,11), np.uint8)
    img = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
    
    #sharpening
    kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])
    img = cv2.filter2D(img, -1, kernel)
    
    #test if you use different scale img than 0.2 of the original that I used
    #remember that the actual kernel size for GaussianBlur is trackbar_pos3*2+1
    #you want to get as full circle as possible here
    #canny = CannyTrackbar(img, "canny_trakbar")
    
    #additional blurring to reduce the offset toward brighter region
    img_blurred = cv2.GaussianBlur(img.copy(), (8*2+1,8*2+1), 1)
    
    #detect edge. important: make sure this works well with CannyTrackbar()
    canny = cv2.Canny(img_blurred, 160, 78)
    
    coords = np.column_stack(np.nonzero(canny))
    
    model, inliers = measure.ransac(coords, measure.CircleModel,
                                    min_samples=3, residual_threshold=1,
                                    max_trials=1000)
    
    rr, cc = draw.circle_perimeter(int(model.params[0]),
                                   int(model.params[1]),
                                   int(model.params[2]),
                                   shape=img.shape)
    
    img[rr, cc] = 1
    
    import matplotlib.pyplot as plt
    plt.imshow(img, cmap='gray')
    plt.scatter(model.params[1], model.params[0], s=50, c='red')
    plt.axis('off')
    plt.savefig('sphere_center.png', bbox_inches='tight')
    plt.show()
    

    Now I'd probably try to calculate where pixels are statisticaly brigher and where they are dimmer to adjust the laser position (if I understand correctly what you're trying to do)

    If the Ransac is still not enough. I'd try tuning Canny to only detect a perfect arc on top of the circle (where it's well outlined) and than try using the following dependencies (I suspect that this should be possible):

    在这里输入图像描述

    链接地址: http://www.djcxy.com/p/79600.html

    上一篇: 通过Canny和Hough的地平线检测

    下一篇: 我如何过滤掉边缘的点