35
votes

In openCV, the low and high thresholds for the canny operator are mandatory:

cvCanny(input,output,thresh1,thresh2)

In Matlab, there's an option to calculate those automatically:

edge(input,'canny')

I've looked into Matlab's code for edge, and this is really not straight forward to calculate those automatically.

Are you aware of any implementation of the canny operator along with automatic threshold calculation for opencv?

thanks

7
Can you post the Matlab code that does this?Adi Shavit

7 Answers

47
votes

I stumbled upon this answer while I was searching for a way to automatically compute Canny's threshold values.

Hope this helps anyone who comes looking for a good method for determining automatic threshold values for Canny's algorithm...


If your image consists of distinct foreground and background, then the edge of foreground object can use extracted by following:

  1. Calculate Otsu's threshold using:

    double otsu_thresh_val = cv::threshold(
        orig_img, _img, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU
    );
    

    We don't need the _img. We are interested in only the otsu_thresh_val but unfortunately, currently there is no method in OpenCV which allows you to compute only the threshold value.

  2. Use the Otsu's threshold value as higher threshold and half of the same as the lower threshold for Canny's algorithm.

    double high_thresh_val  = otsu_thresh_val,
           lower_thresh_val = otsu_thresh_val * 0.5;
    cv::Canny( orig_img, cannyOP, lower_thresh_val, high_thresh_val );
    

More information related to this can be found in this paper: The Study on An Application of Otsu Method in Canny Operator. An explaination of Otsu's implementation can be found here.

17
votes

You can use the mean value of the your input grayscale image and define lower and upper thresholds using standard deviation. You can have more detailed explanation and opencv code here: http://www.kerrywong.com/2009/05/07/canny-edge-detection-auto-thresholding/

7
votes

Also, there is code available to do this automatically, by putting this in the OpenCV build. I found it on the OpenCV-users mailing list, so no guarantees. :)

Discussion: http://opencv-users.1802565.n2.nabble.com/Automatic-thresholding-in-cvCanny-td5871024.html GitHub (code): https://gist.github.com/756833

4
votes

Check out this link: http://www.pyimagesearch.com/2015/04/06/zero-parameter-automatic-canny-edge-detection-with-python-and-opencv/

They implement a similar solution using basic statistics to determine the low and high threshold for Canny edge detection.

def auto_canny(image, sigma=0.33):
     # compute the median of the single channel pixel intensities
     v = np.median(image)

    # apply automatic Canny edge detection using the computed median
    lower = int(max(0, (1.0 - sigma) * v))
    upper = int(min(255, (1.0 + sigma) * v))
    edged = cv2.Canny(image, lower, upper)

    # return the edged image
    return edged
2
votes

I have looked through the source code of Matlab Canny edge detection and I managed to write it in Java with OpenCV 3.

private static Mat getpartialedge(Mat image){
    double nonEdgeRate = 0.6;
    double thresholdRate = 0.6;
    double w = image.cols();
    double h = image.rows();
    int bins = 256;
    Mat sobel = new Mat();
    Mat sobelx = new Mat();
    Mat sobely = new Mat();
    Mat sobelxabs = new Mat();
    Mat sobelyabs = new Mat(); 
    Size gsz = new Size(5, 5);
    if(false) {
        Imgproc.Canny(image, sobel, 41, 71);
    }else {

        //Imgproc.GaussianBlur(graycopy,graycopy, gsz, 2);
        //Imgproc.dilate(image, image, kernel8);
        Imgproc.GaussianBlur(image, image, gsz, 2);


        int apertureSize = 3;
        Imgproc.Sobel(image, sobelx, CvType.CV_16S, 1, 0, apertureSize, 1, 0);
        Core.convertScaleAbs(sobelx, sobelxabs);
        Imgproc.Sobel(image, sobely, CvType.CV_16S, 0, 1, apertureSize, 1, 0);
        Core.convertScaleAbs(sobely, sobelyabs);
        Core.addWeighted(sobelxabs, 1, sobelyabs, 1, 0, sobel);
        sobel.convertTo(sobel, CvType.CV_8U);


        Mat equalized = new Mat();
        Imgproc.equalizeHist(sobel, equalized);
        Imgcodecs.imwrite(filePath + "aftersobel(eq).png", equalized);
        Imgcodecs.imwrite(filePath + "aftersobel.png", sobel);


        Mat hist = new Mat();
        List<Mat> matList = new ArrayList<Mat>();
        matList.add(sobel);
        Imgproc.calcHist(matList, new MatOfInt(0), new Mat(), hist, new MatOfInt(bins), new MatOfFloat(0f, 256f));
        float accu = 0;
        float t = (float) (nonEdgeRate * w * h);
        float bon = 0;
        float[] accutemp = new float[bins];
        for (int i = 0; i < bins; i++) {
            float tf[] = new float[1];
            hist.get(i, 0, tf);
            accu = accu + tf[0];
            accutemp[i] = accu;
            if (accu > t) {
                bon = (float) i;
                break;
            }
        }
        Imgproc.threshold(sobel, sobel, bon, 255, Imgproc.THRESH_BINARY);
        double ut = bon;
        double lt = thresholdRate * bon;


        Imgproc.Canny(image, sobel, lt, ut);
        //Imgproc.dilate(sobel, sobel, kernel2);
    }
    return sobel;
}

The filepath is the place to hold the output images. And the input image should be a gray-scale image with U8 data type. The basic principle is to rule out nonEdgeRate(60%) pixel as non-edge pixel by the brightness. A histogram is used to sort the brightness and the upper threshold will be set so that there are 60% pixels below it. The lower threshold is set by multiplying the upper threshold by the thresholdRate(0.6).

Note that the double nonEdgeRate = 0.6 and double thresholdRate = 0.6 is tuned by myself in my specific use case. Th original values are 0.7 and 0.4 separately in matlab.

1
votes

I have another approach to the same problem. This solution also involves selection of optimal thresholds for edge detection.

  • First compute the median of the gray scale image.
  • Choose two values (lower and upper thresholds) based on the median value of the gray scale image.

The following pseudo-code shows you how its done:

v = np.median(gray_img)
sigma = 0.33

#---- apply optimal Canny edge detection using the computed median----
lower_thresh = int(max(0, (1.0 - sigma) * v))
upper_thresh = int(min(255, (1.0 + sigma) * v))

Fix these thresholds as parameters in the canny edge detection function.

Illustration: If you observe a Gaussian curve in statistics, values between 0.33 from both sides of the curve are considered in the distribution. Any value outside these points are assumed to be outliers. Since images are considered to be data, this concept is assumed here as well.

0
votes

As Luca Del Tongo has suggested, you can calculate the thresholds from the grey image, e.g. in Java using OpenCV...

MatOfDouble mu = new MatOfDouble();
MatOfDouble stddev = new MatOfDouble();
Core.meanStdDev(greyMat, mu, stddev);
threshold1 = mu.get(0, 0)[0];
threshold2 = stddev.get(0, 0)[0];
Imgproc.Canny(greyMat, outputMat, threshold1, threshold2);