简体   繁体   中英

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. The image is actually a numpy array with the shape (128, 128) and elements with real values between [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. 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:

 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]

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.

The image is actually a numpy array with the shape (128, 128) and elements with real values between [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). 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. The documentation says the following about the input image:

Source, an 8-bit single-channel image. Non-zero pixels are treated as 1's. Zero pixels remain 0's, so the image is treated as binary . You can use compare , inRange , threshold , adaptiveThreshold , Canny , and others to create a binary image out of a grayscale or color one. If mode equals to RETR_CCOMP or RETR_FLOODFILL , the input can also be a 32-bit integer image of labels ( 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 :

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). 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 . Since the RETR_LIST mode doesn't generate any hierarchy, you can ignored that return value:

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 ). 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. 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):

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. The error is given because your input image is a grayscale image.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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