OpenCV Assertion failed: (-215:Assertion failed) npoints >= 0 && (depth == CV_32F || depth == CV_32S)
Asked Answered
G

7

19

I have found the following code on this website:

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

When I try to run the code I get the following error:

[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'

I've tried searching for a solution on StackOverflow, but I didn't find anything remotely similar.


EDIT (see comments):

  • type(contour[0]) = <class 'numpy.ndarray'>

  • len(contour) = 4

Godfather answered 17/2, 2019 at 15:6 Comment(5)
Please, add len(contour) and type(contour[0]).Shifra
Thank you very much for the quick reply, I just updated my question.Godfather
Comment this line contours = contours[0] if imutils.is_cv2() else contours[1]Maloriemalory
@BahramdunAdil Thanks for the quick reply, now it giving me the following error: Traceback (most recent call last): File "extract_single_letters_from_captchas.py", line 49, in <module> (x, y, w, h) = cv2.boundingRect(contour) TypeError: Expected cv::UMat for argument 'array'Godfather
@Fozoro I updated my answer with some info on why this happened in case you're curious :)Shifra
S
34

This is doing the wrong thing:

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

imutils.is_cv2() is returning False even though it should return True. If you don't mind to remove this dependency, change to:

contours = contours[0]

I found out the reason. Probably, the tutorial you are following was published before OpenCV 4 was released. OpenCV 3 changed cv2.findContours(...) to return image, contours, hierarchy, while OpenCV 2's cv2.findContours(...) and OpenCV 4's cv2.findContours(...) return contours, hierarchy. Therefore, before OpenCV 4, it was correct to say that if you use OpenCV 2 it should be contours[0] else contours[1]. If you still want to have this "compatibility", you can change to:

contours = contours[1] if imutils.is_cv3() else contours[0]
Shifra answered 17/2, 2019 at 15:27 Comment(4)
@Fozoro glad to help, but now I'm curious why they changed this output on OpenCV 3 :) I'll let you know if I find something about itShifra
@Fozoro I could not figure it out :( The change happened 5 months ago (here's the commit), but there is no pull request or issue linked to it. Sometimes things just change, I guess :)Shifra
Alternatively, you can change to imutils.is_cv2(or_better=True). You will then get True if you are using opencv4Shellacking
@NikO'Lai but this would not address the issue that only OpenCV 3 has a different return formatShifra
H
6

In OpenCV4, cv2.findContours has only 2 return values. Contours being the FIRST value

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

Note that I added underscore to let go of the other return value of hierarchy

Hohenlinden answered 17/4, 2020 at 18:32 Comment(0)
F
4
 (x, y, w, h) = cv2.boundingRect(contour.astype(np.int))
Flyleaf answered 16/9, 2020 at 11:55 Comment(1)
While code-only answers might answer the question, you could significantly improve the quality of your answer by providing context for your code, a reason for why this code works, and some references to documentation for further reading. From How to Answer: "Brevity is acceptable, but fuller explanations are better."Zito
G
1

This is because of opencv-python version 4.0.0. If you want to fix this without changing your code then downgrade opencv-python to version 3.4.9.31

  • Uninstall opencv-python

    pip uninstall opencv-python

  • Install opencv-python==3.4.9.31

    pip install opencv-python==3.4.9.31

If facing issue with function 'pointSetBoundingRect', you need to install 'opencv-python-headless'

pip install opencv-python-headless==3.4.9.31
Gottfried answered 21/2, 2020 at 7:44 Comment(0)
P
1

I wrote the same code in following way:

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

and my code worked. I think previously it was returning 2 variables now we have to unpack into three variables. If this doesn't work try the following:

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

this should work.

For more information, you can visit the OpenCV documentation page: https://docs.opencv.org/3.1.0/d4/d73/tutorial_py_contours_begin.html

I hope this will help you.

Phyte answered 23/5, 2020 at 13:0 Comment(0)
G
0

The reason lays in findContours().

In OpenCV version 3, we wrote:

_, contours, _ = cv.findContours()

In OpenCV version 4, we change to:

contours, _ = cv.findContours()

Use either that fix the problem.

Or, we can stabilize our OpenCV version with these commands, suppose that you had anaconda installed.

conda install -c conda-forge opencv=4.1.0 

pip install opencv-contrib-python  
Gariepy answered 27/2, 2021 at 4:22 Comment(0)
L
-1

【OpenCV 3 changed cv2.findContours(...) to return image, contours, hierarchy】 This content is very helpful for me. I adda new variable to the front and I fix all the error..

Lurleen answered 8/1, 2020 at 6:35 Comment(1)
While this might be a valuable hint to solve the problem, a good answer also demonstrates the solution. Please EDIT to provide example code to show what you mean. Alternatively, consider writing this as a comment insteadDimidiate

© 2022 - 2024 — McMap. All rights reserved.