简体   繁体   English

在 OpenCv (Python) 中查找单通道图像的轮廓

[英]FindContours of a single channel image in OpenCv (Python)

I've trouble using OpenCV's findContours(...) method to find the contours in a single channel image.我在使用 OpenCV 的findContours(...)方法来查找单通道图像中的轮廓时遇到了麻烦。 The image is actually a numpy array with the shape (128, 128) and elements with real values between [0.0,1.0] .该图像实际上是一个形状为(128, 128)的 numpy 数组,元素的实数值在[0.0,1.0]之间。 Initially the shape is (1,128,128,1) but I've used np.squeeze(...) to get rid of the first and last dimension.最初的形状是(1,128,128,1)但我已经使用np.squeeze(...)摆脱了第一个和最后一个维度。 Keeping either of them doesn't solve my problem.保留其中任何一个都不能解决我的问题。

What I've tried:我试过的:

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)

The above code causes the following exception:上面的代码导致以下异常:

error: (-215) scn == 3 || scn == 4 in function cv::cvtColor

What I've also tried:我也尝试过:

If I apply findContours(...) directly, so without using cvtColor(...) , I get a different error:如果我直接应用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

Some sources suggest to use a threshold to get an binary image which is required by findContours(...) [1]一些消息来源建议使用threshold来获取findContours(...) [1]所需的二进制图像

ret, thresh = cv2.threshold(image, 1, 255, 0)
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

This won't help either and I receive the same exception complaining about the CV_8UC1 support.这也无济于事,我收到了同样的异常,抱怨CV_8UC1支持。

The image is actually a numpy array with the shape (128, 128) and elements with real values between [0.0,1.0] .该图像实际上是一个形状为(128, 128)的 numpy 数组,元素的实数值在[0.0,1.0]之间。

The error from cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) was due to the fact that you're trying to convert a single channel image from BGR (3 channels) to grayscale (1 channel).来自cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)的错误是由于您试图将单通道图像从 BGR(3 通道)转换为灰度(1 通道)。 Your image is already grayscale, so this step is unnecessary.你的图像已经是灰度的,所以这一步是不必要的。

The error from cv2.findContours was due to the wrong data type of the elements in the array. cv2.findContours的错误是由于数组中元素的数据类型错误。 The documentation says the following about the input image:文档说明了有关输入图像的以下内容:

Source, an 8-bit single-channel image.源,8 位单通道图像。 Non-zero pixels are treated as 1's.非零像素被视为 1。 Zero pixels remain 0's, so the image is treated as binary .零像素保持 0,因此图像被视为 binary 。 You can use compare , inRange , threshold , adaptiveThreshold , Canny , and others to create a binary image out of a grayscale or color one.您可以使用compareinRangethresholdadaptiveThresholdCanny等从灰度或彩色图像中创建二进制图像。 If mode equals to RETR_CCOMP or RETR_FLOODFILL , the input can also be a 32-bit integer image of labels ( CV_32SC1 ).如果 mode 等于RETR_CCOMPRETR_FLOODFILL ,则输入也可以是标签的 32 位整数图像 ( CV_32SC1 )。

To fix this, you need to scale the values in your image to range [0.0,255.0] , and then cast the result to np.uint8 :要解决此问题,您需要将图像中的值缩放到范围[0.0,255.0] ,然后将结果转换为np.uint8

image_8bit = np.uint8(image * 255)

There are few other issue or quirks about the code in your question.关于您问题中的代码,几乎没有其他问题或怪癖。

First of all, in one snippet cv2.findContours returns 2 values (OpenCV 2.x), and in the other it returns 3 values (OpenCV 3.x).首先,在一个片段中cv2.findContours返回 2 个值 (OpenCV 2.x),在另一个片段中它返回 3 个值 (OpenCV 3.x)。 Which version are you using?您使用的是哪个版本?

Your first code sample contains the following:您的第一个代码示例包含以下内容:

contours, hierarchy = cv2.findContours(image, 1, 2)

Avoid using magic numbers.避免使用幻数。 The 1 corresponds to cv2.RETR_LIST and the 2 corresponds to cv2.CHAIN_APPROX_SIMPLE . 1对应于cv2.RETR_LIST2对应于cv2.CHAIN_APPROX_SIMPLE Since the RETR_LIST mode doesn't generate any hierarchy, you can ignored that return value:由于RETR_LIST模式不生成任何层次结构,您可以忽略该返回值:

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

Another problem is most likely the fact that originally you didn't explicitly binarize the image (eg using cv2.threshold ).另一个问题很可能是最初您没有明确地对图像进行二值化(例如使用cv2.threshold )。 While this won't result in exceptions, the result will probably not make much sense -- findContours divides the pixels into two groups -- zeros, and then everything non-zero.虽然这不会导致异常,但结果可能没有多大意义findContours将像素分为两组——零,然后所有非零。 You will most likely want them partitioned differently.您很可能希望它们以不同的方式分区。

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

Sample script (OpenCV 3.x):示例脚本(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.

Try this:试试这个:

cv2.threshold(image, 1, 255, cv2.THRESH_BINARY,image)
im2, contours, hierarchy = cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

The cv2.findContours function accept thresholded images. cv2.findContours 函数接受阈值图像。 The error is given because your input image is a grayscale image.给出错误是因为您的输入图像是灰度图像。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM