Opencv ellipse() bug? - python

cv.ellipse( img, center, axes, angle, startAngle, endAngle, color[, thickness[, lineType[, shift]]] )
The startAngle is the starting angle of the elliptic arc in degrees.If we set angele=30 and startAngle=60,the angle of the starting line of the ellipse may be 30+60=90.Is that right?
But this is not the case.
My code:
img2 = np.zeros((500, 500))
img2 = cv2.ellipse(img2, (250, 250), (120, 40), 30, 60, 180, 255, -1)
cv2.imshow('2', img2)
cv2.waitKey(0)
Lets look at the result, the angle of the starting line(red line) is obviously not 90 degrees.
enter image description here
Why?Is it a bug?

I got it.
startAngle is not the starting angle based on the ellpise.It is based on the circumcircle of that ellipse.Lets look at the following figure
It does a mapping operation.The blue curve is the output.

Related

To cut a piece from a circle, I have to do the math manually first. Is there a smarter way to do the job using Python along with opencv?

I'm trying to cut a piece from a circle using Python along with opencv, here is the code
firstly, I constructed the circle
layer1 = np.zeros((48, 48, 4))
cv2.circle(layer1, (24, 24), 23, (0, 0, 0, 255), -1)
res = layer1[:]
and I got
and then, I drew a smaller square on it
start_point = (24, 0); end_point = (48, 24); color = (255, 0, 0)
cv2.rectangle(res, start_point, end_point, color, -1)
which gives
similarly, I drew a triangle on the circle
pt1 = (24, 0); pt2 = (48, 0); pt3 = (24, 24)
triangle_cnt = np.array( [pt1, pt2, pt3] )
cv2.drawContours(res, [triangle_cnt], 0, (255,0,0), -1)
which gives
I can go along this way to draw a smaller triangle, 1/16, 1/32 and so on.
I have to do the math manually to get the vertices.
Is there a smarter (more elegant) way to do the job?
import cv2
import numpy as np
# Colors (B, G, R)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
# Create new blank 300x150 white image
width, height = 800, 500
img = np.zeros((height, width, 3), np.uint8)
img[...] = BLACK
center = (width//2, height//2)
axes = (200, 200) # axes radius, keep equal to draw circle.
angle = 0 #clockwise first axis
startAngle = 0
endAngle = 90
color = WHITE
img = cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color, thickness=-1)
cv2.imshow('image', img)
cv2.waitKey(-1)
You can play with startAngle and endAngle to change the position of the white part.
Another option is to change the angle option (to -90 for example to rotate counter clockwise).
EDIT to show the different end angles add
img = cv2.ellipse(img, center, axes, angle, startAngle, endAngle/2, (255, 0, 0), thickness=-1)
img = cv2.ellipse(img, center, axes, angle, startAngle, endAngle/4, (0, 255, 0), thickness=-1)
img = cv2.ellipse(img, center, axes, angle, startAngle, endAngle/8, (0, 0, 255), thickness=-1)

Understanding the ellipse parameters in Open CV using Python

I am trying to draw an arc using Open CV, using cv2.ellipse function
I tried reading the documentation for the same, but I m finding it very confusing. It is an arc in my case so axes_x and axes_y are same, i.e the radius. What should be my axis, In which direction should I calculate the Start and the End angle? And what is this angle of rotation?
Given is the function -
cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color[, thickness[, lineType[, shift]]])
import cv2
import numpy as np
def create_blank(height, width, color):
blank_image = np.zeros((int(height), int(width), 3), np.uint8)
blank_image[:, :] = color
return blank_image
def draw_arc(image):
height, width = image.shape[0:2]
# Ellipse parameters
radius = 100
center = (width / 2, height/2)
axes = (radius, radius)
angle = 0
startAngle = 135
endAngle = 180
cv2.line(image, (0, 150), (300, 150), (0, 0, 0), 2, cv2.CV_AA)
cv2.line(image, (150, 0), (150, 300), (0, 0, 0), 2, cv2.CV_AA)
cv2.ellipse(image, center, axes, angle, startAngle, endAngle, (0, 0, 0), 2, cv2.CV_AA)
cv2.imshow("ellipse", image)
# Create new blank 300x150 white image
width, height = 300, 300
image = create_blank(width, height, color=WHITE)
draw_arc(image)
cv2.waitKey(0)
cv2.destroyAllWindows()
When my startAngle is 135 and endAngle is 180, the result looks like
whereas when the startAngle is 0 and endAngle is 90, the result looks like
So this makes it confusing, in which direction is the arc rotating.
You can really easily view how the change of parameters affect the drawing of the ellipse. Here is a simple code for it:
import numpy as np
import cv2
center = (200, 200) # x,y
axes = (100, 75) # first, second
angle = 0. # clockwise, first axis, starts horizontal
for i in range(360):
image = np.zeros((400, 400, 3)) # creates a black image
image = cv2.ellipse(image, center, axes, angle, 0., 360, (0,0,255))
image = cv2.ellipse(image, center, axes, angle, 0., i, (0,255,0))
cv2.imshow("image", image)
cv2.waitKey(5)
cv2.waitKey(0)
cv2.destroyAllWindows()
This will do something like:
Lets go through the parameters:
center -> x and y tuple where the center of the ellipse is.
axes -> first and second axes radius (half the total size). The first one is the horizontal one if angle 0 is applied, the second one will be the vertical one.
angle -> The angle of the whole ellipse, i.e. if you move clockwise the first axis
startAngle -> where you want to start drawing your arc, for example 0 will be like my example image (in the first axis), but if the angle has a value, then the 0 will rotate the same way.
endAngle -> where you want to stop drawing, you can see that I vary it in my example to draw an increasing ellipse.
If you want an arc of a circle of radius 50px, lets say from 60 degrees up to 120 degrees, but in counterclockwise (360 - start/endAngle) you can do:
image = cv2.ellipse(image, (100,100), (50,50), 0.0, 360-120, 360-60, (0,255,0))
If you have doubts with any of them, feel free to ask in a comment

Customizing cv2.circle

Im using following code to draw circle around face.
for (x, y, w, h) in faces:
cv2.circle(img, ( int((x + x + w )/2), int((y + y + h)/2 )), int (h / 2), (0, 255, 0), 5)
however the thickness of drawn circle is entirely green. Is there any ways to make few percentage (say 30% )of the circle to be pink?
As I suggested in the comments, you could use cv2.ellipse() to draw the two arcs individually. For example:
import numpy as np
import cv2
img = np.ones((400,400,3), np.uint8) * 255
# See:
# http://docs.opencv.org/2.4/modules/core/doc/drawing_functions.html#cv2.ellipse
# http://docs.opencv.org/3.1.0/dc/da5/tutorial_py_drawing_functions.html
circ_center = (200,200)
circ_radius = 150
circ_thick = 12
circ_axes = (circ_radius,circ_radius)
# cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color[, thickness[, lineType[, shift]]])
cv2.ellipse(img, circ_center, circ_axes, 0, 0, 90, (255,0,0), circ_thick, cv2.LINE_AA)
cv2.ellipse(img, circ_center, circ_axes, 0, 90, 360, (0,255,0), circ_thick, cv2.LINE_AA)
cv2.imshow("Image", img)
cv2.imwrite("circ1.png", img)
cv2.waitKey()
Produces:
Now, the arcs have rounded edges. This may or may not be an issue for you. I'm not sure if there's a better way in OpenCV, but one way that I've created thick lines with flat edges is to build the thick lines out of many thin lines.
For example:
import numpy as np
import cv2
img = np.ones((400,400,3), np.uint8) * 255
# See:
# http://docs.opencv.org/2.4/modules/core/doc/drawing_functions.html#cv2.ellipse
# http://docs.opencv.org/3.1.0/dc/da5/tutorial_py_drawing_functions.html
circ_center = (200,200)
circ_radius = 150
circ_thick = 12
def draw_arc(img, center, rad, angle, startAngle, endAngle, color, thickness, lineType, thick=1):
for r in range(rad,rad+thickness):
cv2.ellipse(img, center, (r,r), angle, startAngle, endAngle, color, thick, lineType)
draw_arc(img, circ_center, circ_radius, 0, 0, 90, (255,0,0), circ_thick, cv2.LINE_AA)
draw_arc(img, circ_center, circ_radius, 0, 90, 360, (0,255,0), circ_thick, cv2.LINE_AA)
cv2.imshow("Image", img)
cv2.imwrite("circ2.png", img)
cv2.waitKey()
Produces:
You can adjust the starting and ending points of the coloring by adjusting the startAngle and endAngle parameters. There are a few other parameters you may want to adjust in there, but this should give you an idea of one approach.
You could also just draw a complete circle and layer an arc on top of it corresponding to what you want colored different, which may be easier in the end.

Hough Transform Not working

I'm trying to detect circles from the image using hough transform
my code :
import numpy as np
import cv2
image = cv2.imread("C:/Users/Anmol/Desktop/your_file.bmp")
output = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2,10)
cv2.waitKey(0)
print (circles)
# 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)
stops at the line
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2,10)
I left my code running for about 5 hours but still its not going ahead of this line. It is not giving any error.
Plzz guide me what to do.
I suggest downscaling the input image if it's really large, and running a blur filter. Both of these will speed up Hough tremendously.

Circle not found

I want to detect a circle from a given image. But it just doesn't work the way I want it to. I implemented a circle detection algorithm, which works on some images with a circle but not on the one I want. I tweaked with the parameters, but couldn't get it to work.
import cv2
import numpy as np
# load the image, clone it for output, and then convert it to grayscale
image = cv2.imread("damn-circle.png")
output = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# detect circles in the image
blur = cv2.GaussianBlur(gray,(5,5),0)
circles = cv2.HoughCircles(blur, cv2.HOUGH_GRADIENT, 2, 120)
cv2.imshow("output", np.hstack([blur]))
cv2.waitKey(0)
print circles
# 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([output]))
cv2.waitKey(0)
You're code is almost perfect. It's just that the method CV_HOUGH_GRADIENT sits inside a package, cv (at least for opencv version: 2.4.13). I changed that one line to mention the package and it worked well. You'll have to put specific versions for OpenCV and NumPy if you're still not getting the right result on this simple image. Change your line to be like this:
circles = cv2.HoughCircles(blur, cv2.cv.CV_HOUGH_GRADIENT, 2, 120)
You should get a nice result. At least I just did. image with found Hough circle shown
Edited:
Ah, I didn't understand which image the question was about. I changed parameters for several items particularly the Canny detector param and the radius min/max and the accumulator resolution. I think these params will find what you want:
circles = cv2.HoughCircles(blur, method = cv2.cv.CV_HOUGH_GRADIENT, minDist = 90 , dp = 1, param1 = 3, param2 = 12 , minRadius = 30, maxRadius = 50)
My found image now looks like this: another image with found circle

Categories