web-dev-qa-db-fra.com

Calcul automatique des seuils bas et hauts pour l'opération Canny en opencv

En openCV, les seuils bas et haut pour l'opérateur canny sont obligatoires:

cvCanny(input,output,thresh1,thresh2)

Dans Matlab, il y a une option pour les calculer automatiquement:

Edge(input,'canny')

J'ai regardé le code de Matlab pour Edge, et ce n'est vraiment pas simple de les calculer automatiquement.

Connaissez-vous une implémentation de l'opérateur canny ainsi qu'un calcul de seuil automatique pour opencv?

merci

32
hoffer

Je suis tombé sur cette réponse alors que je cherchais un moyen de calculer automatiquement les valeurs de seuil de Canny.

J'espère que cela aide toute personne qui vient à la recherche d'une bonne méthode pour déterminer les valeurs de seuil automatiques pour l'algorithme de Canny ...


Si votre image se compose d'un premier plan et d'un arrière-plan distincts, l'objet Bord du premier plan peut être extrait en procédant comme suit:

  1. Calculez le seuil d'Otsu en utilisant:

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

    Nous n'avons pas besoin du _img. Nous ne sommes intéressés que par le otsu_thresh_val mais malheureusement, il n'y a actuellement aucune méthode dans OpenCV qui vous permet de calculer uniquement la valeur de seuil.

  2. Utilisez la valeur de seuil d'Otsu comme seuil supérieur et la moitié de la même valeur que le seuil inférieur pour l'algorithme de Canny.

    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 );
    

Plus d'informations à ce sujet peuvent être trouvées dans cet article: L'étude sur une application de la méthode Otsu dans Canny Operator . Une explication de l'implémentation d'Otsu peut être trouvée ici .

46
VP.

Vous pouvez utiliser la valeur moyenne de votre image en niveaux de gris d'entrée et définir des seuils inférieur et supérieur à l'aide de l'écart-type. Vous pouvez avoir des explications plus détaillées et du code opencv ici: http://www.kerrywong.com/2009/05/07/canny-Edge-detection-auto-thresholding/

16
Luca Del Tongo

De plus, du code est disponible pour le faire automatiquement, en le plaçant dans la version OpenCV. Je l'ai trouvé sur la liste de diffusion des utilisateurs d'OpenCV, donc aucune garantie. :)

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

7
Dan

Consultez ce lien: http://www.pyimagesearch.com/2015/04/06/zero-parameter-automatic-canny-Edge-detection-with-python-and-opencv/

Ils implémentent une solution similaire en utilisant les statistiques de base pour déterminer le seuil bas et haut pour la détection Canny Edge.

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
4
Ryan Marten

J'ai parcouru le code source de la détection Matlab Canny Edge et j'ai réussi à l'écrire en Java avec 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;
}

Le chemin de fichier est l'endroit où contenir les images de sortie. Et l'image d'entrée doit être une image en niveaux de gris avec le type de données U8. Le principe de base est d'exclure le pixel nonEdgeRate (60%) comme pixel non Edge par la luminosité. Un histogramme est utilisé pour trier la luminosité et le seuil supérieur sera défini de sorte qu'il y ait 60% de pixels en dessous. Le seuil inférieur est défini en multipliant le seuil supérieur par le seuilRate (0,6).

Notez que le double nonEdgeRate = 0.6 et le double thresholdRate = 0.6 est réglé par moi-même dans mon cas d'utilisation spécifique. Les valeurs d'origine sont 0,7 et 0,4 séparément dans matlab.

3
fangzhan shi

J'ai une autre approche du même problème. Cette solution implique également la sélection de seuils optimaux pour la détection des contours.

  • Calculez d'abord la médiane de l'image en niveaux de gris.
  • Choisissez deux valeurs (seuils inférieur et supérieur) en fonction de la valeur médiane de l'image en niveaux de gris.

Le pseudo-code suivant vous montre comment c'est fait:

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))

Fixez ces seuils en tant que paramètres dans la fonction de détection de bord cany.

Illustration: Si vous observez une courbe gaussienne dans les statistiques, des valeurs comprises entre 0,33 des deux côtés de la courbe sont prises en compte dans la distribution. Toute valeur en dehors de ces points est supposée être des valeurs aberrantes. Étant donné que les images sont considérées comme des données, ce concept est également supposé ici.

1
Jeru Luke

Comme l'a suggéré Luca Del Tongo, vous pouvez calculer les seuils à partir de l'image grise, par ex. dans Java utilisant 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);
0
Buddhisthead