Finding contours from a thresholded image - python

I'm currently reading through the example code on the OpenCV website tyring to find contours in an image.
I first read an image and convert to gray-scale:
img = cv2.imread('/.../.../four.png')
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
I then convert the image into binary by applying a threshold:
thresh = cv2.threshold(imgray, 127, 255, 0, cv2.THRESH_BINARY)
According to the tutorials.. I should then be able to call findContours() on the thresholded image:
contours = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
When trying to execute this code, for some reason i'm getting a type error:
contours =
cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
TypeError: image is not a numerical tuple
Unsure why?
Here's the full code for easier readability:
img = cv2.imread('/Users/samtozer/Desktop/four.png')
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(imgray, 127, 255, 0, cv2.THRESH_BINARY)
contours = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, contours, -1, (0,255,0), 3)
Wondering if anyone has experienced this problem before? And if so, what is going wrong xD
Thanks in advance

As mentioned in the comments there are two issues that you have to look out for:
Return type of cv2.findContours()
There are two return values in cv2.findContours():
Contours present in the image
The hierarchy of these contours
Return type of cv2.threshold()
There are two return values in cv2.threshold():
Return value. (It returns float value of the threshold value which is used to classify the pixel values)
Thresholded image

Related

Missing part of a contour in finding Contour opencv (affected by white background, document scanner)

I am trying to do perspective transform for documents in various background and light conditions. Currently, I can't do it on this image because the contour is not close.
The original Image
Edge Detection of image
So far, I have tried Adaptive Threshold, GaussianBlur, MedianBlur,all kind of Morphology operations, Hough Transform and Histogram Equalization and play with all of the parameters in them but nothing has worked.
Can anyone help me with this issue ?
Use Canny Edges, Dilation and Erosion getting this output
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(img,20,40)
kernel = np.ones((5,5),np.uint8)
dilation = cv2.dilate(edges,kernel,iterations = 1)
erosion = cv2.erode(dilation,kernel,iterations = 1)
ret,thresh = cv2.threshold(erosion, 200, 255, 0)
contours, hierarchy = cv2.findContours(thresh,
cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
largest_areas = sorted(contours, key=cv2.contourArea)
cv2.drawContours(img, [largest_areas[-2]], -1, 255, 2)
cv2.imshow("image", img)
cv2.waitKey(0)

Counting special elements on image with OpenCV and Python

I want to count number of trees on this picture from above.
I know how to count elements, but till now I used images with white background, so counting is much easier. But on image like this i do not know what to do:
I converted the image to gray, and then done the threshold *(threshold value is done by hand, is there a way to find it automaticly?), my next idea is to find the 'centers' of black dots, or to 'group' them.
I also tried to change brightness and contrast but it didnt work.
What should I do?
This is the code that I wrote:
import cv2
import numpy as np
# Read image
img = cv2.imread('slika.jpg')
# Convert image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Show grayscale image
cv2.imshow('gray image', gray)
cv2.waitKey(0)
#BIG PROBLEM: IM FINDING VALUE OF `40` IN THE LINE BELOW MANUALLY
# Inverse binary threshold image with threshold at 40,
_, threshold_one = cv2.threshold(gray, 40 , 255, cv2.THRESH_BINARY_INV)
# Show thresholded image
cv2.imshow('threshold image', threshold_one)
cv2.waitKey(0)
# Find contours
contours, h = cv2.findContours(threshold_one, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
print('Number of trees found:', len(contours)) #GIVES WRONG RESULT
# Iterate all found contours
for cnt in contours:
# Draw contour in original/final image
cv2.drawContours(img, [cnt], 0, (0, 0, 255), 1)
# Show final image
cv2.imshow('result image', img)
cv2.waitKey(0)
This is the image with treshold, I have tried to blur it (in order to connect black dots), but the final output is the same:
This is the result image:
Here's a rough method to estimate the number of trees. The idea to to model each tree as a blob then use contour filtering with a minimum threshold area to ignore the noise. To determine automatic threshold levels, you can use Otsu's threshold by appending cv2.THRESH_OTSU or Adaptive threshold with cv2.adaptiveThreshold(). This approach has problems when the trees are very close together since they form a single blob. Possible improvements could be to find the average area of each tree then find the floor with a large blob. You would probably need to train a classifier and use deep/machine learning for better accuracy
Trees: 102
import cv2
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)
opening = cv2.morphologyEx(close, cv2.MORPH_OPEN, kernel, iterations=2)
cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
trees = 0
for c in cnts:
area = cv2.contourArea(c)
if area > 50:
x,y,w,h = cv2.boundingRect(c)
cv2.drawContours(image, [c], -1, (36,255,12), 2)
trees += 1
print('Trees:', trees)
cv2.imshow('image', image)
cv2.waitKey()

cv2.findContours function is not working in both versions

I am new to computer vision and haven't really went through any tutorials on thresholding or blurring or other filters.
I am using the below two piece of codes which finds out the contours in an image. On one hand the method is working but on the other it is not. I would need help in understanding the reason this is happening so as to convince myself what is happening in the background.
Working code snippet:
img=cv2.imread('path.jpg')
imgBlurred = cv2.GaussianBlur(img, (5, 5), 0)
gray = cv2.cvtColor(imgBlurred, cv2.COLOR_BGR2GRAY)
sobelx = cv2.Sobel(gray, cv2.CV_8U, 1, 0, ksize=3)
cv2.imshow("Sobel",sobelx)
cv2.waitKey(0)
ret2, threshold_img = cv2.threshold(sobelx, 120, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
im2, contours, hierarchy = cv2.findContours(threshold_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
Not working code snippet
# read image
src = cv2.imread(file_path, 1)
# show source image
cv2.imshow("Source", src)
# convert image to gray scale
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
# blur the image
blur = cv2.blur(gray, (3, 3))
# binary thresholding of the image
ret, thresh = cv2.threshold(blur, 200, 255, cv2.THRESH_BINARY)
# find contours
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
I would really appreciate if anyone can find out the reason for the wrong which is happening here.
The error that i am facing is:
Traceback (most recent call last): File "convexhull.py", line 27, in
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) ValueError: not enough values to unpack
(expected 3, got 2)
Let me know if any other information is also required.
This is due to a change in openCV. Since version 4.0 findContours returns only 2 values: the contours and the hierarchy. Before, in version 3.x, it returned 3 values. You can use the documentation to compare the different versions.
The second code snippet should work when you change your code to:
# find contours
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
Why the first snippet picks a different openCV version can't be determined from the information given.
The following snippet will work irrespective of the OpenCV version installed in your system/environment and will also store all the tuples in individual variables that can be used later in the code.
Store the major version of OpenCV installed:
import cv2
major_version = cv2.__version__[0]
Based on the version either of the following two statements will be executed and the corresponding variables will be populated:
if major_version == '4':
contours, hierarchy = cv2.findContours(image_binary, cv2.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
elif major_version == '3':
image, contours, hierarchy = cv2.findContours(image_binary, cv2.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
The contours returned from the function in either scenarios will be stored in contours.

How to find area of a curve in python where coordinates are not known?

So I am currently working on a project where I need to find the area in terms of cm of a particular curve.The problem is the curve has more than one colors each representing a different values
Something Like This
Can someone help me do it? There are more than one such curves in the image. How to simultaneously calculate all of them in Python.
You can use the following code to print area in terms of pixels. To get the area in cm^2, you need to know the relationship between pixels and actual length.
The following code prints the area of largest blob in the image.
To get the area of all the blobs in the image, just replace [c] with contours
import cv2
import numpy as np
img = cv2.imread("image.png", 0)
blank = np.zeros_like(img)
ret, thresh = cv2.threshold(img, 0 ,255, cv2.THRESH_BINARY)
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
if( len(contours) != 0 ):
c = max(contours, key = cv2.contourArea)
cv2.drawContours(blank, [c], -1, 255, -1)
print cv2.countNonZero(blank)
cv2.imshow("img", blank)
cv2.waitKey(0)
cv2.destroyAllWindows()
Edit:
import cv2
import numpy as np
img = cv2.imread("images.png", 0)
blank = np.zeros_like(img)
ret, thresh = cv2.threshold(img, 0 ,255, cv2.THRESH_BINARY)
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for i in range(len(contours)):
cv2.drawContours(blank, contours[i], -1, 255, -1)
print "area of contour " + str(i)+" = " + str(cv2.contourArea(contours[i]))
cv2.imshow("img", blank)
cv2.waitKey(0)
cv2.destroyAllWindows()
1, separate out the different colored blobs. They look like they are generated by some other mapping software so presumably the colors are fixed and known. Make a new image for each color
2, For an image that contains only blobs of a fixed color and a black background you can make contours of the outlines (see findContours). Opencv will give you a separate contour for each blob.
3, Calculate the area of each contour -there is an opencv function to do this.

How to save OpenCV image with contour

I want to save image with contour
Here is my code:
img = cv2.imread('123.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
image, contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
# some code in here
cv2.imwrite('234.jpg', cnt)
Thanks a lot.
What you want to do is to create a mask that you draw the contours on to, then use that to snip out the rest of the picture, or vice-versa. For instance, based on this tutorial:
(contours, _) = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
mask = np.ones(img.shape[:2], dtype="uint8") * 255
# Draw the contours on the mask
cv2.drawContours(mask, contours, -1, 0, -1)
# remove the contours from the image and show the resulting images
img = cv2.bitwise_and(img, img, mask=mask)
cv2.imshow("Mask", mask)
cv2.imshow("After", img)
cv2.waitKey(0)
The easiest way to save the contour as image is taking out its ROI(region of image) and saving it using imwrite() as follows -
First use cv2.boundingRect to get the bounding rectangle for a set of points (i.e. contours):
x, y, width, height = cv2.boundingRect(contours[i])
You can then use NumPy indexing to get your ROI from the image:
roi = img[y:y+height, x:x+width]
And save the ROI to a new file:
cv2.imwrite("roi.png", roi)
I was trying many times and finally, I could make it:
image= cv2.imread('muroprueba.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
cnts, herarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image,cnts,-1,(0,255,0),1)
cv2.imshow('image1',image)
cv2.waitKey(0)
cv2.imwrite('F:\caso1.jpg',image) #Save the image
cv2.destroyAllWindows()

Categories