OpenCV断言失败:(-215:断言失败)npoints> = 0 &&(depth == CV_32F || depth == CV_32S)

18

我在这个网站上找到了以下代码:

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

当我尝试运行代码时,出现以下错误:
[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'

我曾在StackOverflow上尝试寻找解决方案,但没有找到任何相似的内容。


编辑(参见评论):

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

  • len(contour) = 4


请添加 len(contour)type(contour[0]) - Berriel
非常感谢您的快速回复,我刚刚更新了我的问题。 - Nazim Kerimbekov
将此行注释掉 contours = contours[0] if imutils.is_cv2() else contours[1] - Bahramdun Adil
@BahramdunAdil 感谢您的快速回复,现在它给我以下错误: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' - Nazim Kerimbekov
@Fozoro 我更新了我的回答,并提供了一些关于为什么会发生这种情况的信息,以防你感兴趣 :) - Berriel
7个回答

34

这样做是错误的:

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

imutils.is_cv2()返回的是False,但它应该返回True。如果您不介意删除该依赖项,请更改为:

imutils.is_cv2()返回的结果是错误的,应该返回True。如果您可以接受移除此依赖项,请更改为以下代码:

contours = contours[0]
我找到了原因。 可能你正在遵循的教程是在OpenCV 4发布之前发表的。 OpenCV 3将 cv2.findContours(...) 改为返回 image, contours, hierarchy,而 OpenCV 2 的 cv2.findContours(...)OpenCV 4 的 cv2.findContours(...) 返回 contours, hierarchy。因此,在OpenCV 4之前,如果您使用OpenCV 2,应该选择 contours[0],否则选择 contours[1] 是正确的。如果您仍希望具有此“兼容性”,则可以更改为:
contours = contours[1] if imutils.is_cv3() else contours[0]

@Fozoro 很高兴能帮忙,但我现在很好奇他们为什么改变了OpenCV 3的输出 :) 如果我找到有关它的信息,我会让你知道。 - Berriel
@Fozoro 我没能想明白 :( 这个更改是在5个月前发生的(这是提交记录),但是没有与之相关联的拉取请求或问题。有时候事情就是会突然变化,我猜 :) - Berriel
2
或者,您可以更改为 imutils.is_cv2(or_better=True)。如果您正在使用opencv4,则会得到True - Nik O'Lai
@NikO'Lai但这并不能解决只有OpenCV 3具有不同返回格式的问题。 - Berriel

6
在OpenCV4中,cv2.findContours 只有两个返回值。 而轮廓(Contours)是第一个返回值。
contours, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

请注意,我添加了下划线以放弃层级的其他返回值。

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

1
虽然仅提供代码的答案可能回答了问题,但是通过为您的代码提供上下文、解释代码工作原理的原因以及一些参考文献来进一步阅读,您可以显著提高答案的质量。从[答案]:“简洁是可以接受的,但更详细的解释更好。” - Pranav Hosangadi

1
这是因为opencv-python版本4.0.0。如果你想在不改变代码的情况下修复它,那么就将opencv-python降级到版本3.4.9.31。
- 卸载opencv-python - pip uninstall opencv-python - 安装opencv-python==3.4.9.31 - pip install opencv-python==3.4.9.31
如果遇到“pointSetBoundingRect”函数问题,则需要安装“opencv-python-headless”。
pip install opencv-python-headless==3.4.9.31

1
我用如下方式编写了相同的代码:

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

我的代码已经可以运行了。我认为之前它只返回了两个变量,现在我们需要解包成三个变量。如果这不起作用,请尝试以下操作:

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

我能帮忙翻译,以下是翻译的结果:

这应该可以工作。

更多信息,请访问OpenCV文档页面:https://docs.opencv.org/3.1.0/d4/d73/tutorial_py_contours_begin.html

希望这对您有所帮助。


0

原因在于findContours()函数。

在OpenCV 3版本中,我们写道:

_, contours, _ = cv.findContours()

在OpenCV 4版本中,我们进行了更改:
contours, _ = cv.findContours()

使用其中一种方法来解决问题。

或者,我们可以使用以下命令来稳定我们的 OpenCV 版本,假设您已经安装了anaconda

conda install -c conda-forge opencv=4.1.0 

pip install opencv-contrib-python  

-1

【OpenCV 3将cv2.findContours(...)的返回值改为图像、轮廓和层次结构】 这篇内容对我非常有帮助。我在前面添加了一个新变量,并修复了所有错误。


1
虽然这可能是解决问题的有价值提示,但一个好的答案也应该展示解决方案。请编辑并提供示例代码以展示您的意思。或者,考虑将其编写为评论。 - ρяσѕρєя K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接