cv2.ellipse seems to have a mind of its own about which way it interprets start-and end angles. I want to draw a circular arc from 300° to 130° in the clockwise direction, i.e. like this:
with this code:
import numpy as np
import cv2
center = (200, 200) # x,y
axes = (100, 100) # first, second
angle = 0. # clockwise, first axis, starts horizontal
for i in range( 330,130,-1):
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, 330, i, (0,255,0))
cv2.imshow("image", image)
cv2.waitKey(5)
cv2.waitKey(0)
cv2.destroyAllWindows()
I got this instead, with the green segment being drawn counter-clockwise from 330 to 130 instead of clockwise.
The code generates the red circle first and then overlays the green arc, but it demonstrates the problem.
Note: You should have probably mentioned that your code comes from [SO]: Understanding the ellipse parameters in Open CV using Python (@api55's answer).
Everything is a bit complicated since the display xOy system is flipped vertically ((+∞ on) Y axis is pointing down) from what we are used to (on paper), the origin is the top left corner instead of the bottom left one (and the 1st quadrant is corresponding to the 4th).
Given a circle, and 2 points on it A and B, where each point name is also the degrees (A, B ∈ [0, 360)
), there are 2 ways to move from A to B (on the circle), as there are 2 arches. Choosing which arch to take has the following implications:
How much to move (distance):
Arch 1 (let this be the shorter): D1 = abs(B - A)
- as distance (even in angles) is always positive
Arch 2: D2 = 360 - D1
- the remainder of the circle
Direction to move to (CCW, CW). This translates to the angle increment being added / subtracted to / from the initial value (A).
Here, I must also mention that moving from B to A is like moving from A to B but in the opposite direction
The 2 things I mentioned above (combined with the fact that CCW is the default direction), translate into the 2 if clauses in the modified version of your code (below). Maybe the conditions are not very obvious (that's why I tried expanding them in the commented lines - code has same effect), but I couldn't find a nicer version (yet).
Anyway, it should work for any angles and directions.
code00.py:
#!/usr/bin/env python
import sys
import numpy as np
import cv2
def main(*argv):
center = (200, 200)
axes = (100, 100)
angle = 0.
start = 330
end = 130
cw = 0 #len(sys.argv) > 1 # @TODO cfati: ClockWise?
if start < end:
steps = 360 + start - end
else:
steps = start - end
if cw:
steps = 360 - steps
cw_factor = 1
else:
cw_factor = -1
"""
# The above if conditions, expanded (same effect)
if start < end:
if cw:
steps = end - start
cw_factor = 1
else:
steps = 360 + start - end
cw_factor = -1
else:
if cw:
steps = 360 - start + end
cw_factor = 1
else:
steps = start - end
cw_factor = -1
"""
step = 1
base_color = (0, 0, 255)
active_color = (0, 255, 0)
image = np.zeros((400, 400, 3))
for i in range(1, steps, step):
cv2.ellipse(image, center, axes, angle, 0, 360, base_color)
cv2.ellipse(image, center, axes, angle, start, start + cw_factor * i, active_color)
cv2.imshow("image", image)
cv2.waitKey(1)
print("Close drawing window to exit...")
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == "__main__":
print(
"Python {:s} {:03d}bit on {:s}\n".format(
" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32,
sys.platform,
)
)
rc = main(*sys.argv[1:])
print("\nDone.\n")
sys.exit(rc)