How to detect ellipse and remove outliers in image using opencv Python - python

I'm trying to extract the dots that form an ellipse then draw it . But because of some points that can be considered as outliers, I got an invalid mask of ellipse. Like this:
Here is the code that I'm executing, but it always selects the outlier
`cv2.rectangle(cleanedpartiallyimage, (0, 0), (1200, 10), (0, 0, 0), -1)
cv2.rectangle(cleanedpartiallyimage, (0, 0), (47, 1200), (0, 0, 0), -1)
image = cv2.cvtColor(cleanedpartiallyimage, cv2.COLOR_BGR2HSV) lower = np.array([85, 0, 20], dtype="uint8")
upper = np.array([95, 255, 255], dtype="uint8") mygray = cv2.inRange(image, lower, upper)
#--- Gaussian and Canny filters to make it easy to get the contours
blurred = cv2.GaussianBlur(mygray, (5, 5), 0) imageCanny = cv2.Canny(blurred, 0, 100, 0)
ret,th = cv2.threshold(imageCanny,127,255, 0)
#--- Find all the contours in the binary image ---
contours,hierarchy = cv2.findContours(th,3,1)
cnt = contours big_contour = [] max = 0 for i in cnt:
area = cv2.contourArea(i) #--- find the contour having biggest area ---
if(area > max): max = area big_contour = i
final = cv2.drawContours(imageCanny, big_contour, -1, (0,255,0), 3)
actualcontours, hierarchy = cv2.findContours(final, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
#---Removing side contour points
actualcontours = getactualcontours(actualcontours, 60)
empty = np.zeros((image.shape[0], image.shape[1], 3), np.uint8)
#---Removes linear contour points
ConvexHullPoints = contoursConvexHull(actualcontours)
#---Converts the points to Ellipse using fitEllipse
test41 = cv2.polylines(empty, [ConvexHullPoints], True, (255, 255, 255), 3)
imageCannyee = cv2.Canny(test41, 0, 100, 0)
contours, hierarchy = cv2.findContours(imageCannyee, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
for cont in contours:
if len(cont) < 20:
break
elps = cv2.fitEllipse(cont)
anotherempty = np.zeros((image.shape[0], image.shape[1], 3), np.uint8)
#---Drawing the ellipse into the empty mask
cv2.ellipse(anotherempty, elps, (255, 255, 255), 2) plt.imshow(anotherempty)

Here's a simple approach:
Obtain binary image. We load the image, convert to grayscale, Gaussian blur, then Otsu's threshold to obtain a binary image.
Dilate to form single contour. Next we create an elliptical shaped kernel using cv2.getStructuringElement with the cv2.MORPH_ELLIPSE parameter and dilate to combine small individual contours into a single large contour.
Identify ellipse. Next we find contours, filter using contour area and then detect the ellipse with cv2.fitEllipse().
import cv2
# Load image, grayscale, Gaussian blur, Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
# Dilate with elliptical shaped kernel
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
dilate = cv2.dilate(thresh, kernel, iterations=2)
# Find contours, filter using contour threshold area, draw ellipse
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
area = cv2.contourArea(c)
if area > 5000:
ellipse = cv2.fitEllipse(c)
cv2.ellipse(image, ellipse, (36,255,12), 2)
cv2.imshow('thresh', thresh)
cv2.imshow('dilate', dilate)
cv2.imshow('image', image)
cv2.waitKey()

Related

How do I differentiate individual objects in an image, when they are clustered together and calculate object size

I'm currently trying to measure the size of woodchips within an image using openCV and a reference object(the coin)
(https://i.stack.imgur.com/GpdJE.jpg).
It mostly works but when the woodchips are close to one another find countours recognizes it as one larger contour. I recently read about the watershed algorithm which can be used to segment the objects more efficiently. However how do I implementent it and simulatanously calculate the height and with of each object relative to the reference object?
Here is my code for measuring the woodchip size
# Read image and preprocess (read an image and convert it it no grayscale)
image = cv2.imread(img_path)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255,
cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
#Blur the image using Gaussian Kernel to remove un-necessary edges
#blur = cv2.GaussianBlur(gray, (9, 9), 0)
#blur = cv2.medianBlur(gray, 7)
blur = cv2.bilateralFilter(thresh,11, 125, 125)
#nbw = cv2.fastNlMeansDenoising(image, None, 10, 7, 21)
#Edge detection using Canny edge detector
edged = cv2.Canny(blur, 50, 100)
edged = cv2.dilate(edged, None, iterations=2)
edged = cv2.erode(edged, None, iterations=2)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (6,6))
closing = cv2.morphologyEx(edged, cv2.MORPH_CLOSE, kernel)
#show_images([blur, edged])
# Find contours
cnts = cv2.findContours(closing.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
# Sort contours from left to right as leftmost contour is reference object
(cnts, _) = contours.sort_contours(cnts)
# Remove contours which are not large enough , Perform morphological closing operation to remove noisy contours
cnts = [x for x in cnts if cv2.contourArea(x) > 400]
# Reference object dimensions
# Here for reference I have used a 2cm x 2cm square
ref_object = cnts[0]
box = cv2.minAreaRect(ref_object)
box = cv2.boxPoints(box)
box = np.array(box, dtype="int")
box = perspective.order_points(box)
(tl, tr, br, bl) = box
dist_in_pixel = euclidean(tl, tr)
dist_in_cm = 2
pixel_per_cm = dist_in_pixel/dist_in_cm
# Draw remaining contours
measurement=[]
for cnt in cnts:
box = cv2.minAreaRect(cnt)
box = cv2.boxPoints(box)
box = np.array(box, dtype="int")
box = perspective.order_points(box)
(tl, tr, br, bl) = box
cv2.drawContours(image, [box.astype("int")], -1, (0, 0, 255), 2)
mid_pt_horizontal = (tl[0] + int(abs(tr[0] - tl[0])/2), tl[1] + int(abs(tr[1] - tl[1])/2))
mid_pt_verticle = (tr[0] + int(abs(tr[0] - br[0])/2), tr[1] + int(abs(tr[1] - br[1])/2))
wid = euclidean(tl, tr)/pixel_per_cm
ht = euclidean(tr, br)/pixel_per_cm
measurement.append((wid,ht))
cv2.putText(image, "{:.1f}cm".format(wid), (int(mid_pt_horizontal[0] - 15), int(mid_pt_horizontal[1] - 10)),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 2)
cv2.putText(image, "{:.1f}cm".format(ht), (int(mid_pt_verticle[0] + 10), int(mid_pt_verticle[1])),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 2)
cv2_imshow(image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
(https://i.stack.imgur.com/nmaW3.jpg)

Improve contour detection

The bubbles are the target of contour detection
I would like to detect the contour of the bubble in the photo. However, no matter what kind of contour detection I used, my program would not detect the whole contour lines. I tried with erosion, blur and morphologyEx methods to make the contour continuous, but it seems the bubble shape has changed completely.
May I ask is there other method that can help me correctly draw the bubble contour similar as original photo?
My code is as following:
cv2.imshow(self.window_name, clone)
canvas = cv2.imshow('canvas', canvas)
# waiting for the user to press any key
imageThresh = cv2.adaptiveThreshold(cloneG, 200, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 3, 11)
bubble_only = cv2.bitwise_and(imageThresh, imageThresh, mask = VA_mask)
thresh = cv2.adaptiveThreshold(bubble_only, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 3, 9)
kernel1 = np.ones((2,2), np.uint8)
# Perform morphological hit-or-miss for
erosion = cv2.morphologyEx(thresh, cv2.MORPH_ERODE, kernel1, iterations = 2)
# Inverted this image and blurred it with a kernel size of 4
ret, thresh1 = cv2.threshold(erosion, 127, 255, 1)
blur = cv2.blur(thresh1, (4,4))
# Again perform another threshold on this image to get the central portion of the edge
ret, thresh2 = cv2.threshold(blur, 145, 255, 0)
# Perform morphological dilation to thin the edge. Use an ellipse structuring element of kernel size of 2
kernel2 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (1,1))
final = cv2.morphologyEx(thresh2, cv2.MORPH_ERODE, kernel2, iterations = 2)
# find the contour then fill the contour
mask = np.zeros(clone.shape[:2], dtype = 'uint8')
contours, hier = cv2.findContours(final, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
bubble_shade = cv2.drawContours(mask, contours, -1, color = (255,255,255), thickness=cv2.FILLED)
area = []
for contour in contours:
cnt_Area = cv2.contourArea(contour)
area.append(cnt_Area)
print(sorted(area))
cv2.imwrite('bubble_shade.jpg', bubble_shade)
cv2.waitKey()
cv2.destroyAllWindows()
return clone
Detected bubble contour

Getting more than expected contours in image

I have a image consisting N almost 90 times but I am getting 105 contours using below code:
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#cv2.imshow("Image", gray)
#cv2.waitKey(0)
blurred = cv2.GaussianBlur(gray, (5, 5), 0) #blur to reduce noise
#cv2.imshow("Image", blurred)
#cv2.waitKey(0)
# perform edge detection, find contours in the edge map, and sort the
# resulting contours from left-to-right
edged = cv2.Canny(blurred, 30, 150) #30, 150
#cv2.imwrite("test.png", edged)
#cv2.imshow("Image", edged)
#cv2.waitKey(0)
#find contours of characters(objects) in images
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
if cnts:
cnts = sort_contours(cnts, method="left-to-right")[0]
cv2.drawContours(image, cnts, -1, (0, 0, 255), 2)
cv2.imwrite("all_contours.jpg", image)
I have tried different combinations in Canny and findContours function but cant get the contours equal to the number of N in image.
My image:
image with contours:
Looking at the contours image, I cant see where is the problem.
Any help or hint will be appreciated.
P.S : this image is an ideal image for testing. In real scenario, image will be taken from webcam.
Try without Bluring image.
import cv2
image = cv2.imread("n.png")
image = cv2.resize(image, (800, 800))
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, threshold = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY_INV)
contours, hierarchy = cv2.findContours(threshold, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cv2.putText(image, "contours found in image : " + str(len(contours)), (20, 20), cv2.FONT_HERSHEY_PLAIN, 1, (255, 0, 0),1)
cv2.drawContours(image, contours, -1, (0, 0, 255), -1)
cv2.imshow("contour_image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV - Detect points along a curve

I need to detect the points along these curves, in particular I need the position on the image of one of the two points starting from the left:
I tried to detect Hough Points like that:
import cv2
import numpy as np
# detect circles in the image
output = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, 100)
# ensure at least some circles were found
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
# loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circles:
# draw the circle in the output image, then draw a rectangle
# corresponding to the center of the circle
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
# show the output image
cv2.imshow("output", np.hstack([image, output]))
cv2.waitKey(0)
But it doesn't get the right points and I suppose that this is because the points are positioned along the two curves. Is there a way to detect points along a curve?
Just found the solution following the comment of #HansHirse. I followed these steps:
Color threshold
Erosion
Finding contours
This is the code:
import cv2
import numpy as np
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv_image, lowerb=(85, 0, 0), upperb=(95, 255, 255))
# Masking with green
imask = mask > 0
green = np.zeros_like(hsv_image, np.uint8)
green[imask] = hsv_image[imask]
kernel = np.ones((8, 8), np.uint8)
erosion = cv2.erode(green, kernel, iterations=1)
gray = cv2.cvtColor(erosion, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Filter out large non-connecting objects
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
area = cv2.contourArea(c)
if area < 500:
cv2.drawContours(thresh,[c],0,0,-1)
# Morph open using elliptical shaped kernel
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=3)
# Find circles
cnts = cv2.findContours(opening, cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)[-2]
for c in cnts:
area = cv2.contourArea(c)
if area > 0 and area < 50:
((x, y), r) = cv2.minEnclosingCircle(c)
cv2.circle(image, (int(x), int(y)), int(r), (36, 255, 12), 2)
cv2.imshow('image', image)
cv2.waitKey()
The result is the following image where the dots are represented by the green circles

How can I draw two contours on an image given a contour distance of 100 pixels between them using python and opencv

What I have tried is drawing the external contour using the following lines
cnts, hierarchy = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(orig, cnts, -1, (0, 255, 0), 3) # this draws the external contour
Referring the below
[![enter image description here][1]][1]
How can I arrive at the answer below?
[![enter image description here][2]][2]
I don't know how it is solved in the links but you can use a blank mask on which you can draw your contour and then use cv2.dilate to expand it using a kernel size of the number of pixels you require between them. Once that is done find contours on the mask and draw the second one onto your original image.
import cv2
import numpy as np
img = cv2.imread('contour.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
cnts, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, cnts, -1, (0, 255, 0), 3)
mask = np.zeros(img.shape[:2], dtype=np.uint8)
cv2.drawContours(mask, cnts, -1, 255, 1)
kernel = np.ones((100, 100), np.uint8)
mask = cv2.dilate(mask, kernel, iterations = 1)
cnts, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, cnts, 1, (255, 0, 0), 3)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Categories