OpenCv (Python) 中单通道图像的 FindContours

Bas*_*ian 2 python opencv numpy

我无法使用 OpenCV 的findContours(...)方法在单通道图像中查找轮廓。该图像实际上是一个 numpy 数组,其形状(128, 128)和元素的实际值介于 之间[0.0,1.0]。最初的形状是(1,128,128,1)但我用过np.squeeze(...)去掉第一个和最后一个维度。保留其中任何一个都不能解决我的问题。

我尝试过的:

image = np.squeeze(array) #using np.squeeze(array, [0]) won't help.
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
contours, hierarchy = cv2.findContours(image, 1, 2)
Run Code Online (Sandbox Code Playgroud)

上面的代码会导致以下异常:

error: (-215) scn == 3 || scn == 4 in function cv::cvtColor
Run Code Online (Sandbox Code Playgroud)

我也尝试过:

如果我直接申请findContours(...),那么不使用cvtColor(...),我会得到一个不同的错误:

 error: (-210) [Start]FindContours supports only CV_8UC1 images when mode != CV_RETR_FLOODFILL otherwise supports CV_32SC1 images only in function cvStartFindContours_Impl
Run Code Online (Sandbox Code Playgroud)

一些来源建议使用 a来获取[1]threshold所需的二进制图像findContours(...)

ret, thresh = cv2.threshold(image, 1, 255, 0)
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
Run Code Online (Sandbox Code Playgroud)

这也无济于事,我收到同样的异常抱怨支持CV_8UC1

Dan*_*šek 5

该图像实际上是一个 numpy 数组,其形状(128, 128)和元素的实际值介于 之间[0.0,1.0]

错误cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)是由于您尝试将单通道图像从 BGR(3 通道)转换为灰度(1 通道)。您的图像已经是灰度的,所以这一步是不必要的。

错误cv2.findContours是由于数组中元素的数据类型错误造成的。该文档对输入图像进行了以下说明:

源,8 位单通道图像。非零像素被视为 1。零像素仍为 0,因此图像被视为二进制。您可以使用compareinRangethresholdadaptiveThresholdCanny等从灰度或彩色图像创建二值图像。如果 mode 等于RETR_CCOMPRETR_FLOODFILL,则输入也可以是标签 ( ) 的 32 位整数图像CV_32SC1

要解决此问题,您需要将图像中的值缩放到 range [0.0,255.0],然后将结果转换为np.uint8

image_8bit = np.uint8(image * 255)
Run Code Online (Sandbox Code Playgroud)

您问题中的代码几乎没有其他问题或怪癖。

首先,在一个片段中cv2.findContours返回 2 个值 (OpenCV 2.x),在另一个片段中返回 3 个值 (OpenCV 3.x)。您使用哪个版本?

您的第一个代码示例包含以下内容:

contours, hierarchy = cv2.findContours(image, 1, 2)
Run Code Online (Sandbox Code Playgroud)

避免使用幻数。对应1cv2.RETR_LIST, 对应2cv2.CHAIN_APPROX_SIMPLE。由于该RETR_LIST模式不生成任何层次结构,因此您可以忽略该返回值:

contours, _ = cv2.findContours(binarized, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
Run Code Online (Sandbox Code Playgroud)

另一个问题很可能是最初您没有明确对图像进行二值化(例如使用cv2.threshold)。虽然这不会导致异常,但结果可能没有多大意义 -findContours将像素分为两组 - 零,然后所有非零。您很可能希望对它们进行不同的分区。

threshold_level = 127 # Set as you need...
_, binarized = cv2.threshold(image_8bit, threshold_level, 255, cv2.THRESH_BINARY)
Run Code Online (Sandbox Code Playgroud)

示例脚本(OpenCV 3.x):

import numpy as np
import cv2


# Generate random image matching your description:
# shape is (128,128), values are real numbers in range [0,1]
image = np.random.uniform(0, np.nextafter(1,2), (128,128))

# Scale and convert data type
image_8bit = np.uint8(image * 255)

threshold_level = 127 # Set as you need...
_, binarized = cv2.threshold(image_8bit, threshold_level, 255, cv2.THRESH_BINARY)

_, contours, hierarchy = cv2.findContours(binarized, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

# ... processing the contours, etc.
Run Code Online (Sandbox Code Playgroud)