#!/usr/bin/python3 # -*- coding: utf-8 -*- from __future__ import division, print_function, unicode_literals import numpy as np def approx_ellipse_perimeter(a, b): lmbd = (max(a, b) - min(a, b)) / (a + b) return (a + b) * np.pi * (1 + 3 * lmbd ** 2 / (10 + np.sqrt(4 - 3 * lmbd ** 2))) def find_nearest_point_on_ellipse(semi_major, semi_minor, p): ######################################################################## # # # This Code is based on https://github.com/0xfaded/ellipse_demo # # See Carl Chatfield's blog for an excellent explanation: # # https://wet-robots.ghost.io/simple-method-for-distance-to-ellipse/ # # This function is licensed under an MIT-License: # # https://github.com/0xfaded/ellipse_demo/blob/master/LICENSE # # # ######################################################################## px = abs(p[0]) py = abs(p[1]) tx = 0.707 ty = 0.707 a = semi_major b = semi_minor for x in range(0, 3): x = a * tx y = b * ty ex = (a*a - b*b) * tx**3 / a ey = (b*b - a*a) * ty**3 / b rx = x - ex ry = y - ey qx = px - ex qy = py - ey r = np.hypot(ry, rx) q = np.hypot(qy, qx) tx = min(1, max(0, (qx * r / q + ex) / a)) ty = min(1, max(0, (qy * r / q + ey) / b)) t = np.hypot(ty, tx) tx /= t ty /= t return np.copysign(a * tx, p[0]), np.copysign(b * ty, p[1]) def rotate_point(p, pivot, angle): # Move to pivot tp = p[0] - pivot[0], p[1] - pivot[1] # Rotate pr = (tp[0] * np.cos(angle / 180 * np.pi) - tp[1] * np.sin(angle / 180 * np.pi), tp[0] * np.sin(angle / 180 * np.pi) + tp[1] * np.cos(angle / 180 * np.pi)) # Move back return pr[0] + pivot[0], pr[1] + pivot[1] if __name__ == "__main__": contour = np.array([ [[200, 100]], [[250, 30]], [[350, 100]], [[440, 300]], [[420, 390]], [[300, 330]], [[200, 200]]], dtype=np.int) ellipse = ((320, 210), (200, 400), -30) import cv2 cvs = np.zeros((512, 512, 3), dtype=np.uint8) cv2.rectangle(cvs, (int(ellipse[0][0]-1), int(ellipse[0][1]-1)), (int(ellipse[0][0]+1), int(ellipse[0][1]+1)), (20, 255, 255), 1) cv2.drawContours(cvs, [contour], 0, (255, 0, 0), 1) rotated_cnt = [] for p in contour: rotated_cnt.append([rotate_point(p[0], ellipse[0], -ellipse[2]+90)]) cv2.drawContours(cvs, [np.array(rotated_cnt, dtype=np.int)], 0, (255, 255, 0), 1) cv2.ellipse(cvs, (ellipse[0], ellipse[1], 90), (128, 0, 128), 1) cv2.ellipse(cvs, ellipse, (255, 0, 255), 1) for p in contour: tp = p[0][0] - ellipse[0][0], p[0][1] - ellipse[0][1] cv2.rectangle( cvs, (int(tp[0]-1), int(tp[1]-1)), (int(tp[0]+1), int(tp[1]+1)), (20, 5, 100), 1) rtp = rotate_point(tp, (0, 0), -ellipse[2]+90) cv2.rectangle( cvs, (int(rtp[0]-1), int(rtp[1]-1)), (int(rtp[0]+1), int(rtp[1]+1)), (20, 50, 200), 1) poe = find_nearest_point_on_ellipse(ellipse[1][1] / 2, ellipse[1][0] / 2, rtp) cv2.rectangle(cvs, (int(poe[0] - 1), int(poe[1] - 1)), (int(poe[0] + 1), int(poe[1] + 1)), (128, 0, 128), 1) poer = rotate_point(poe, (0, 0), ellipse[2]-90) cv2.rectangle(cvs, (int(poer[0] - 1), int(poer[1] - 1)), (int(poer[0] + 1), int(poer[1] + 1)), (150, 0, 30), 1) poert = poer[0] + ellipse[0][0], poer[1] + ellipse[0][1] cv2.rectangle(cvs, (int(poert[0] - 1), int(poert[1] - 1)), (int(poert[0] + 1), int(poert[1] + 1)), (255, 255, 255), 1) dist = np.sqrt((p[0][0] - poert[0]) ** 2 + (p[0][1] - poert[1]) ** 2) print(dist) cv2.imshow('Ellipse test', cvs) key = cv2.waitKey(0) cv2.destroyAllWindows()