9
votes

Échec de l'assertion OpenCV: (-215: échec de l'assertion) npoints> = 0 && (profondeur == CV_32F || profondeur == CV_32S)

J'ai trouvé le code suivant sur ce site Web :

[INFO] processing image 1/9955
Traceback (most recent call last):
  File "extract_single_letters_from_captchas.py", line 47, in <module>
    (x, y, w, h) = cv2.boundingRect(contour)
cv2.error: OpenCV(4.0.0) /Users/travis/build/skvark/opencv-python/opencv/modules/imgproc/src/shapedescr.cpp:741: error: (-215:Assertion failed) npoints >= 0 && (depth == CV_32F || depth == CV_32S) in function 'pointSetBoundingRect'

Lorsque j'essaye d'exécuter le code, j'obtiens l'erreur suivante:

import os
import os.path
import cv2
import glob
import imutils
CAPTCHA_IMAGE_FOLDER = "generated_captcha_images"
OUTPUT_FOLDER = "extracted_letter_images"


# Get a list of all the captcha images we need to process
captcha_image_files = glob.glob(os.path.join(CAPTCHA_IMAGE_FOLDER, "*"))
counts = {}

# loop over the image paths
for (i, captcha_image_file) in enumerate(captcha_image_files):
    print("[INFO] processing image {}/{}".format(i + 1, len(captcha_image_files)))

    # Since the filename contains the captcha text (i.e. "2A2X.png" has the text "2A2X"),
    # grab the base filename as the text
    filename = os.path.basename(captcha_image_file)
    captcha_correct_text = os.path.splitext(filename)[0]

    # Load the image and convert it to grayscale
    image = cv2.imread(captcha_image_file)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # Add some extra padding around the image
    gray = cv2.copyMakeBorder(gray, 8, 8, 8, 8, cv2.BORDER_REPLICATE)

    # threshold the image (convert it to pure black and white)
    thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]

    # find the contours (continuous blobs of pixels) the image
    contours = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # Hack for compatibility with different OpenCV versions
    contours = contours[0] if imutils.is_cv2() else contours[1]

    letter_image_regions = []

    # Now we can loop through each of the four contours and extract the letter
    # inside of each one
    for contour in contours:
        # Get the rectangle that contains the contour
        (x, y, w, h) = cv2.boundingRect(contour)

        # Compare the width and height of the contour to detect letters that
        # are conjoined into one chunk
        if w / h > 1.25:
            # This contour is too wide to be a single letter!
            # Split it in half into two letter regions!
            half_width = int(w / 2)
            letter_image_regions.append((x, y, half_width, h))
            letter_image_regions.append((x + half_width, y, half_width, h))
        else:
            # This is a normal letter by itself
            letter_image_regions.append((x, y, w, h))

    # If we found more or less than 4 letters in the captcha, our letter extraction
    # didn't work correcly. Skip the image instead of saving bad training data!
    if len(letter_image_regions) != 4:
        continue

    # Sort the detected letter images based on the x coordinate to make sure
    # we are processing them from left-to-right so we match the right image
    # with the right letter
    letter_image_regions = sorted(letter_image_regions, key=lambda x: x[0])

    # Save out each letter as a single image
    for letter_bounding_box, letter_text in zip(letter_image_regions, captcha_correct_text):
        # Grab the coordinates of the letter in the image
        x, y, w, h = letter_bounding_box

        # Extract the letter from the original image with a 2-pixel margin around the edge
        letter_image = gray[y - 2:y + h + 2, x - 2:x + w + 2]

        # Get the folder to save the image in
        save_path = os.path.join(OUTPUT_FOLDER, letter_text)

        # if the output directory does not exist, create it
        if not os.path.exists(save_path):
            os.makedirs(save_path)

        # write the letter image to a file
        count = counts.get(letter_text, 1)
        p = os.path.join(save_path, "{}.png".format(str(count).zfill(6)))
        cv2.imwrite(p, letter_image)

        # increment the count for the current key
        counts[letter_text] = count + 1
  • type (contour [0]) =

  • len (contour) = 4


5 commentaires

Veuillez ajouter len (contour) et type (contour [0]) .


Merci beaucoup pour la réponse rapide, je viens de mettre à jour ma question.


Commentez cette ligne contours = contours [0] if imutils.is_cv2 () else contours [1]


@BahramdunAdil Merci pour la réponse rapide, maintenant cela me donne l'erreur suivante: Traceback (dernier appel en dernier): Fichier "extract_single_letters_from_captchas.py", ligne 49, dans (x, y, w, h) = cv2.boundingRect (contour) TypeError: cv :: UMat attendu pour l'argument 'array'


@Fozoro J'ai mis à jour ma réponse avec quelques informations sur les raisons pour lesquelles cela s'est produit au cas où vous seriez curieux :)


6 Réponses :


17
votes

Cela ne fait pas la bonne chose:

contours = contours[1] if imutils.is_cv3() else contours[0]

imutils.is_cv2 () renvoie False même s'il doit renvoyer Vrai . Si cela ne vous dérange pas de supprimer cette dépendance, changez en:

contours = contours[0]

J'ai trouvé la raison. Le didacticiel que vous suivez a probablement été publié avant la sortie d'OpenCV 4. OpenCV 3 a changé cv2.findContours (...) pour renvoyer image, contours, hiérarchie , tandis que OpenCV 2 cv2.findContours (...) et OpenCV 4's cv2.findContours (...) a> renvoie contours, hiérarchie . Par conséquent, avant OpenCV 4, il était correct de dire que si vous utilisez OpenCV 2, il devrait s'agir de contours [0] sinon de contours [1] . Si vous souhaitez toujours avoir cette "compatibilité", vous pouvez passer à:

contours = contours[0] if imutils.is_cv2() else contours[1]


4 commentaires

@Fozoro est heureux de vous aider, mais maintenant je suis curieux de savoir pourquoi ils ont changé cette sortie sur OpenCV 3 :) Je vous ferai savoir si je trouve quelque chose à ce sujet


@Fozoro Je n'ai pas pu le comprendre :( Le changement s'est produit il y a 5 mois (voici le Vous pouvez également passer à imutils.is_cv2 (or_better = True) . Vous obtiendrez alors True si vous utilisez opencv4


@ NikO'Lai mais cela ne résoudrait pas le problème que seul OpenCV 3 a un format de retour différent




1
votes

Ceci est dû à la version 4.0.0 d'opencv-python. Si vous souhaitez résoudre ce problème sans changer votre code, rétrograder opencv-python à la version 3.4.9.31

  • Désinstaller opencv-python

    pip désinstaller opencv-python

  • Installez opencv-python == 3.4.9.31

    pip install opencv-python == 3.4.9.31

Si vous rencontrez un problème avec la fonction 'pointSetBoundingRect', vous devez installer 'opencv-python-headless'

pip install opencv-python-headless==3.4.9.31


0 commentaires

1
votes

Dans OpenCV4, cv2.findContours n'a que 2 valeurs de retour. Contours étant la PREMIÈRE valeur

contours, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

Notez que j'ai ajouté un trait de soulignement pour supprimer l'autre valeur de retour de hiérarchie


0 commentaires

0
votes

J'ai écrit le même code de la manière suivante:

_, contours, _ = cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)

et mon code a fonctionné. Je pense qu'avant, il retournait 2 variables maintenant que nous devons décompresser en trois variables. Si cela ne fonctionne pas, essayez ce qui suit:

_, contours, hierarchy = cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)

cela devrait fonctionner.

Pour plus d'informations, vous pouvez visiter la page de documentation OpenCV: https://docs.opencv.org/3.1.0/d4/d73/tutorial_py_contours_begin.html

J'espère que cela vous aidera.


0 commentaires

1
votes
 (x, y, w, h) = cv2.boundingRect(contour.astype(np.int))

1 commentaires

Bien que les réponses de code uniquement puissent répondre à la question, vous pouvez améliorer considérablement la qualité de votre réponse en fournissant un contexte pour votre code, une raison pour laquelle ce code fonctionne et des références à la documentation pour une lecture plus approfondie. De Comment répondre : "La concision est acceptable, mais des explications plus complètes sont meilleures."