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
Related
I've got images like this one:
I need to detect the center of this circular element:
(More precisely - I'm looking for the midpoint of the circular element)
Currently my code detect the mold (the plastic circle that holds the circular element) and select the rectangle ROI to focus the image into the relevant area:
import cv2
import imutils
import numpy as np
if __name__ == "__main__":
image = cv2.imread('Dart - Overview Image - with Film.bmp')
img = imutils.resize(image, width=700)
image = img
output = image.copy()
roi = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# cv2.imshow("Gray", gray)
# cv2.waitKey(0)
# detect circles in the image
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), 2)
cv2.rectangle(output, (x - 2, y - 2), (x + 2, y + 2), (0, 128, 255), -1)
roi = roi[y - r: y + r, x - r: x + r]
cv2.imshow("img", roi)
cv2.waitKey(0)
This will show this image:
Now, I'm trying to find the midpoint of the circular element inside this ROI image by detecting the circular element but It's challenged for me, I've try this methods:
gray_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray_roi, (3, 3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)[1]
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
for c in cnts:
(x, y), radius = cv2.minEnclosingCircle(c)
cv2.circle(roi, (int(x), int(y)), int(radius), (35, 255, 12), 3)
cv2.circle(roi, (int(x), int(y)), 1, (35, 255, 12), 2)
print(x, y)
break
# Find Canny edges
edged = cv2.Canny(roi, 30, 121, apertureSize=3, L2gradient=True)
cv2.waitKey(0)
# Finding Contours
# Use a copy of the image e.g. edged.copy()
# since findContours alters the image
contours, hierarchy = cv2.findContours(edged,
cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cv2.imshow('Canny Edges After Contouring', edged)
cv2.waitKey(0)
And got this output:
I've also tried to change the threshold parameters in the cv2.Canny function but it's didn't give me better results.
Thanks a lot!
I am currently doing a project on painting recognition using opencv-python.
Right now I am able to detect most of the paintings decently however the bounding boxes are rectangles that include a lot of background.
This is because the cv2.boundingRect() function finds the bounding rectangle with a perpendicular projection (afaik). However I want to find the best bounding box without detecting any background.
The main part of my code:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
hue, saturation, value = cv2.split(hsv)
blurred_sat = cv2.GaussianBlur(saturation, (5, 5), 0)
edges = cv2.Canny(blurred_sat, 45, 100)
kernel = np.ones((3, 3), np.uint8)
dilate = cv2.dilate(edges, kernel, iterations=6)
erode = cv2.erode(dilate, kernel, iterations=2)
contours, hierarchy = cv2.findContours(erode, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = sorted(contours, key=cv2.contourArea, reverse=True)[:10]
# cv2.drawContours(frame, contours, -1, (255, 255, 0), 3)
for contour in contours:
area = cv2.contourArea(contour)
if area >= 3000:
x, y, w, h = cv2.boundingRect(contour)
subImg = frame[y:y+h, x:x+w]
cv2.rectangle(frame,
(x, y), (x + w, y + h),
(0, 255, 0),
2)
Current output image (video)
Desired output image with desired bounding box in red
After finding and sorting the contours, you need to use cv2.minAreaRect() function. This draws a rectangle enclosing each contour with the least area:
import numpy as np
# portion of code to find and sort contours
for contour in contours:
area = cv2.contourArea(contour)
if area >= 3000:
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(frame, [box], 0, (0, 255, 0), 2)
Note: cv2.minAreaRect() returns properties of the rotated rectangle (center, dimensions and angle of rotation). cv2.boxPoints() is used to obtain the vertices of the rectangle. This is then passed into cv2.drawContours() to draw them.
Have a look at the documentation
For details on the algorithm:
Link 1
Link 2
Update:
The following code approximates a quadrilateral around a contour and draws it on frame. The variable value determines how strong you want your approximations to be:
# use a value between 0 and 1
value = 0.02
for c in contours:
perimeter = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, value*perimeter, True)
if len(approx) == 4:
cv2.drawContours(frame, [approx], 0, (0, 0, 255), 5)
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()
I have images similar to the one below. First of all, I am trying to detect the curves in these images. The curves I want to capture are marked on the image. Next, I want to fit these curves into the circle. I will use the radii of these circles as result.
But I have problem with detecting curves in images. Any help much appreciated. Thanks in advance.
Input Image
Expected
Here's the code I'm using to detect and draw the curves:
import cv2
import numpy as np
from skimage.feature import peak_local_max
from skimage.morphology import watershed
from scipy import ndimage
image = cv2.imread("croppedImage.png")
img = cv2.medianBlur(image,13)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_MEAN_C,\
cv2.THRESH_BINARY,45,0)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,3))
kernel1 = np.ones((3, 3), np.uint8)
kernel2 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
dilate = cv2.dilate(thresh, kernel1, iterations=1)
erode = cv2.erode(dilate, kernel,iterations=1)
# Remove small noise by filtering using contour area
cnts = cv2.findContours(erode, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
if cv2.contourArea(c) < 800:
if len(c)>0:
cv2.drawContours(thresh,[c], 0, (0,0,0), -1)
# Compute Euclidean distance from every binary pixel
# to the nearest zero pixel then find peaks
distance_map = ndimage.distance_transform_edt(erode)
local_max = peak_local_max(distance_map, indices=False, min_distance=1, labels=thresh)
# Perform connected component analysis then apply Watershed
markers = ndimage.label(local_max, structure=np.ones((3, 3)))[0]
labels = watershed(-distance_map, markers, mask=erode)
# Iterate through unique labels
for label in np.unique(labels):
if label == 0:
continue
# Create a mask
mask = np.zeros(thresh.shape, dtype="uint8")
mask[labels == label] = 255
# Find contours and determine contour area
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
c = max(cnts, key=cv2.contourArea)
cv2.drawContours(image, [c], -1, (36,255,12), -1)
cv2.imwrite('Results/drawedImage.png',image)
thresh = 155
im_bw = cv2.threshold(image, thresh, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite("Results/binary.png",im_bw)
Result Image
Binary Result
From the images like below, I can fit circles. But I don't have clean images like this one.
gray_blurred = cv2.GaussianBlur(img,(11,11),0)
ret3,thresh= cv2.threshold(gray_blurred,100,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# Apply Hough transform on the blurred image.
detected_circles = cv2.HoughCircles(thresh,
cv2.HOUGH_GRADIENT, 1, 80, param1 = 20,
param2 = 9, minRadius = 120, maxRadius = 200)
# Draw circles that are detected.
if detected_circles is not None:
# Convert the circle parameters a, b and r to integers.
detected_circles = np.uint16(np.around(detected_circles))
for pt in detected_circles[0, :]:
a, b, r = pt[0], pt[1], pt[2]
# Draw the circumference of the circle.
cv2.circle(img, (a, b), r, (0, 255, 0), 2)
# Draw a small circle (of radius 1) to show the center.
cv2.circle(img, (a, b), 1, (0, 0, 255), 3)
else:
print("Circle is not found")
I inserted an ellipse into the image, I know its center and I would like to add a line that divides the ellipse into two halves. This is my actuall code:
import cv2
import imutils
img = cv2.imread('labrador.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 100 , 255, cv2.THRESH_BINARY)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cnt = max(cnts, key=cv2.contourArea)
ellipse = cv2.fitEllipse(cnt)
cv2.ellipse(img, ellipse, (0, 255, 0), 3)
x, y = ellipse[0]
cv2.circle(img, (int(x), int(y)), 10, (255, 255, 255), -1)
cv2.imshow("dog_with_ellipse", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Original image:
Original image with an ellipse
And result should look like:
How could I do that? And how to calculate the two points that could be used to divide an ellipse by length?
Add rotated labrador image:
Here is one way in Python/OpenCV. I note that the findEllipse() function returns the center, minor and major axes diameters and the angle.
Input:
import cv2
import numpy as np
import math
# read input
img = cv2.imread('labrador.jpg')
# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold
thresh = cv2.threshold(gray, 100 , 255, cv2.THRESH_BINARY)[1]
# find largest contour
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
# fit contour to ellipse and get ellipse center, minor and major diameters and angle in degree
ellipse = cv2.fitEllipse(big_contour)
(xc,yc),(d1,d2),angle = ellipse
print(xc,yc,d1,d1,angle)
# draw ellipse
result = img.copy()
cv2.ellipse(result, ellipse, (0, 255, 0), 3)
# draw circle at center
xc, yc = ellipse[0]
cv2.circle(result, (int(xc),int(yc)), 10, (255, 255, 255), -1)
# draw vertical line
# compute major radius
rmajor = max(d1,d2)/2
if angle > 90:
angle = angle - 90
else:
angle = angle + 90
print(angle)
xtop = xc + math.cos(math.radians(angle))*rmajor
ytop = yc + math.sin(math.radians(angle))*rmajor
xbot = xc + math.cos(math.radians(angle+180))*rmajor
ybot = yc + math.sin(math.radians(angle+180))*rmajor
cv2.line(result, (int(xtop),int(ytop)), (int(xbot),int(ybot)), (0, 0, 255), 3)
# save results
cv2.imwrite("labrador_ellipse.jpg", result)
cv2.imshow("labrador_thresh", thresh)
cv2.imshow("labrador_ellipse", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
ADDITION:
For this image:
the code generates this result: