def create_polygon(ellipse): ellipse1 = Ellipse((ellipse["center_x"], ellipse["center_y"]), ellipse["radius_x"] * 2, ellipse["radius_y"] * 2, ellipse["theta"]) vertices = ellipse1.get_verts() polygon = Polygon(vertices) return polygon
def error_ellipse(self, i, j, nstd=1, space_factor=5.0, clr="b", alpha=0.5, lw=1.5): """ return the plot of the error ellipse from the covariance matrix use ideas from error_ellipse.m from http://www.mathworks.com/matlabcentral/fileexchange/4705-error-ellipse ( the version used here was taken from an earlier version, it looks to have been updated there to a new version.) * (i,j) specify the ith and jth parameters to be used and * nstd specifies the number of standard deviations to plot, the default is 1 sigma. * space_factor specifies the number that divides the width/height, the result of which is then added as extra space to the figure """ def eigsorted(cov): vals, vecs = np.linalg.eigh(cov) order = vals.argsort()[::1] return vals[order], vecs[:, order] self.marginalize(param_list=[i, j]) vals, vecs = eigsorted(self.marginal_covariance_matrix) theta = np.degrees(np.arctan2(*vecs[:, 0][::-1])) # print theta width, height = 2 * nstd * np.sqrt(vals) xypos = [self.parameter_values[i], self.parameter_values[j]] ellip = Ellipse(xy=xypos, width=width, height=height, angle=theta, color=clr, alpha=alpha) # ellip.set_facecolor("white") ellip.set_linewidth(lw) ellip_vertices = ellip.get_verts() xl = [ellip_vertices[k][0] for k in range(len(ellip_vertices))] yl = [ellip_vertices[k][1] for k in range(len(ellip_vertices))] dx = (max(xl) - min(xl)) / space_factor dy = (max(yl) - min(yl)) / space_factor xyaxes = [min(xl) - dx, max(xl) + dx, min(yl) - dy, max(yl) + dy] return ellip, xyaxes
def error_ellipse(self, i, j, nstd=1, space_factor=5., clr='b', alpha=0.5, lw=1.5): """ return the plot of the error ellipse from the covariance matrix use ideas from error_ellipse.m from http://www.mathworks.com/matlabcentral/fileexchange/4705-error-ellipse ( the version used here was taken from an earlier version, it looks to have been updated there to a new version.) * (i,j) specify the ith and jth parameters to be used and * nstd specifies the number of standard deviations to plot, the default is 1 sigma. * space_factor specifies the number that divides the width/height, the result of which is then added as extra space to the figure """ def eigsorted(cov): vals, vecs = np.linalg.eigh(cov) order = vals.argsort()[::1] return vals[order], vecs[:,order] self.marginalize(param_list=[i,j]) vals, vecs = eigsorted(self.marginal_covariance_matrix) theta = np.degrees(np.arctan2(*vecs[:,0][::-1])) #print theta width, height = 2* nstd * np.sqrt(vals) xypos=[self.parameter_values[i], self.parameter_values[j]] ellip = Ellipse(xy=xypos, width=width, height=height, angle=theta, color=clr, alpha=alpha) #ellip.set_facecolor("white") ellip.set_linewidth(lw) ellip_vertices=ellip.get_verts() xl=[ellip_vertices[k][0] for k in range(len(ellip_vertices))] yl=[ellip_vertices[k][1] for k in range(len(ellip_vertices))] dx=(max(xl)-min(xl))/space_factor dy=(max(yl)-min(yl))/space_factor xyaxes=[min(xl)-dx, max(xl)+dx, min(yl)-dy, max(yl)+dy] return ellip, xyaxes
def get_ellipse_path(params): cx = params[0] cy = params[1] a = params[2] b = params[3] alpha = params[4] ell = Ellipse((cx, cy), a * 2., b * 2., alpha) coord = ell.get_verts() xs = coord[:, 0] ys = coord[:, 1] return xs, ys
def fitEllipse(cont, name): x = cont[:, 0] y = cont[:, 1] x = x[:, None] y = y[:, None] #create an array, D, of elliptical polynomial values from points #a1x**2+a2xy+a3y**2+a4x+a5y+a6=0 D = numpy.hstack([x * x, x * y, y * y, x, y, numpy.ones(x.shape)]) #perform a series of linear algebra steps to find lagrangian multipliers. S = numpy.dot(D.T, D) C = numpy.zeros([6, 6]) C[0, 2] = C[2, 0] = 2 C[1, 1] = -1 E, V = numpy.linalg.eig(numpy.dot(numpy.linalg.inv(S), C)) n = numpy.argmax(E) #Output various polynomial parameters for best fit stored in array, 'a'. a = V[:, n] #-------------------Fit ellipse------------------- b, c, d, f, g, a = a[1] / 2., a[2], a[3] / 2., a[4] / 2., a[5], a[0] #Use a number of geometric identities to convert the polynomial coefficients to major and minor axis, center point, and tilt angle values. num = b * b - a * c cx = (c * d - b * f) / num cy = (a * f - b * d) / num angle = 0.5 * numpy.arctan(2 * b / (a - c)) * 180 / numpy.pi up = 2 * (a * f * f + c * d * d + g * b * b - 2 * b * d * f - a * c * g) down1 = (b * b - a * c) * ((c - a) * numpy.sqrt(1 + 4 * b * b / ((a - c) * (a - c))) - (c + a)) down2 = (b * b - a * c) * ((a - c) * numpy.sqrt(1 + 4 * b * b / ((a - c) * (a - c))) - (c + a)) a = numpy.sqrt(abs(up / down1)) b = numpy.sqrt(abs(up / down2)) #---------------------Get path--------------------- #returns a matplotlib patch associated with an ellipse. ell = Ellipse((cx, cy), a * 2., b * 2., angle, lw=4, fill=False, color=(numpy.random.randint(100) / 100, numpy.random.randint(100) / 100, numpy.random.randint(100) / 100), label=name) ell_coord = ell.get_verts() #params values: cx=center point x value, cy=center point y value, a=major axis length, b=minor axis length, angle=tilt angle params = [cx, cy, a, b, angle] return params, ell_coord, ell
def fitEllipseCorrected(cont): # https://stackoverflow.com/questions/39693869/fitting-an-ellipse-to-a-set-of-data-points-in-python x = cont[:, 0] y = cont[:, 1] x = x[:, None] y = y[:, None] D = np.hstack([x * x, x * y, y * y, x, y, np.ones(x.shape)]) S = np.dot(D.T, D) C = np.zeros([6, 6]) C[0, 2] = C[2, 0] = 2 C[1, 1] = -1 E, V = np.linalg.eig(np.dot(np.linalg.inv(S), C)) # if method==1: # n=numpy.argmax(numpy.abs(E)) # else: # n = np.argmax(E) a = V[:, n] #-------------------Fit ellipse------------------- b, c, d, f, g, a = a[1] / 2., a[2], a[3] / 2., a[4] / 2., a[5], a[0] num = b * b - a * c cx = (c * d - b * f) / num cy = (a * f - b * d) / num angle = 0.5 * np.arctan(2 * b / (a - c)) * 180 / np.pi up = 2 * (a * f * f + c * d * d + g * b * b - 2 * b * d * f - a * c * g) down1 = (b * b - a * c) * ((c - a) * np.sqrt(1 + 4 * b * b / ((a - c) * (a - c))) - (c + a)) down2 = (b * b - a * c) * ((a - c) * np.sqrt(1 + 4 * b * b / ((a - c) * (a - c))) - (c + a)) a = np.sqrt(abs(up / down1)) b = np.sqrt(abs(up / down2)) #---------------------Get path--------------------- ell = Ellipse((cx, cy), a * 2., b * 2., angle) ell_coord = ell.get_verts() params = [cx, cy, a, b, angle] return params, ell_coord
def fitEllipse(contour): x = contour[:,0] y = contour[:,1] x = x[:,None] y = y[:,None] D = np.hstack([x*x,x*y,y*y,x,y,np.ones(x.shape)]) S = np.dot(D.T,D) C = np.zeros([6,6]) C[0,2] = C[2,0] = 2 C[1,1] = -1 E, V = np.linalg.eig(np.dot(np.linalg.inv(S),C)) n = np.argmax(E) a = V[:,n] #fit ellipse b,c,d,f,g,a = a[1]/2., a[2], a[3]/2., a[4]/2., a[5], a[0] num = b*b-a*c cx = (c*d-b*f)/num cy = (a*f-b*d)/num angle = 0.5*np.arctan(2*b/(a-c))*180/np.pi up = 2*(a*f*f+c*d*d+g*b*b-2*b*d*f-a*c*g) down1 = (b*b-a*c)*( (c-a)*np.sqrt(1+4*b*b/((a-c)*(a-c)))-(c+a)) down2 = (b*b-a*c)*( (a-c)*np.sqrt(1+4*b*b/((a-c)*(a-c)))-(c+a)) a = np.sqrt(abs(up/down1)) b = np.sqrt(abs(up/down2)) ell = Ellipse((cx,cy),a*2.,b*2.,angle) ell_coord = ell.get_verts() params = [cx,cy,a,b,angle] return params,ell_coord
def update_ellipse(self, datd, dats, nodepth=False): """Update error ellipse plot.""" self.figure.clear() x = np.ma.masked_invalid(datd['1_longitude']) y = np.ma.masked_invalid(datd['1_latitude']) xmin = x.min()-0.5 xmax = x.max()+0.5 ymin = y.min()-0.5 ymax = y.max()+0.5 self.axes = self.figure.add_subplot(111) # , projection=ccrs.PlateCarree()) self.axes.set_xlim(xmin, xmax) self.axes.set_ylim(ymin, ymax) # extent = [xmin, xmax, ymax, ymin] # request = cimgt.GoogleTiles(style='satellite') # self.axes.set_extent(extent, crs=ccrs.PlateCarree()) # self.axes.add_image(request, 6) # self.axes.gridlines(draw_labels=True) for dat in dats: if 'E' not in dat: continue lon = dat['1'].longitude lat = dat['1'].latitude erx = dat['E'].longitude_error ery = dat['E'].latitude_error erz = dat['E'].depth_error cvxy = dat['E'].cov_xy cvxz = dat['E'].cov_xz cvyz = dat['E'].cov_yz if nodepth is True: cvxz = 0 cvyz = 0 erz = 0 cov = np.array([[erx*erx, cvxy, cvxz], [cvxy, ery*ery, cvyz], [cvxz, cvyz, erz*erz]]) if True in np.isnan(cov): continue vals, vecs = eigsorted(cov) abc = (2*np.sqrt(abs(vals)) * np.cos(np.arctan2(vecs[2, :], np.sqrt(vecs[0, :]**2+vecs[1, :]**2)))) if abc[0] == abc.max(): ang = np.rad2deg(np.arctan2(vecs[1, 0], vecs[0, 0])) if abc[1] == abc.max(): ang = np.rad2deg(np.arctan2(vecs[1, 1], vecs[0, 1])) if abc[2] == abc.max(): ang = np.rad2deg(np.arctan2(vecs[1, 2], vecs[0, 2])) abc[::-1].sort() # sort in reverse order emaj = abc[0] emin = abc[1] # approx conversion to degrees demin = emin/110.93 # lat demaj = emaj/(111.3*np.cos(np.deg2rad(lat+demin))) # long from lat ell = Ellipse(xy=(lon, lat), width=demaj, height=demin, angle=ang, color='black') ell.set_facecolor('none') # ell.set_edgecolor('black') # self.axes.add_artist(ell) self.ellipses.append(ell.get_verts()) self.axes.add_artist(ell) self.figure.tight_layout() self.figure.canvas.draw()
def __init__(self,*args): ''' A least-squares fitted ellipse. Parameters ---------- *args : An arraylike of points XY (shape Nx2), or two arraylikes of ordinates, X and Y (shape N) The points against which to fit the ellipse. Raises ------ np.linalg.LinAlgError Raised when there are an insufficient number of points to fit the ellipse, or when the resultant matrix is singular. Attributes ------- centre_x: x ordinate of ellipse centre centree_y: y ordinate of ellipse centre centre: (centre_x,centre_y) angle: rotation of the ellipse from horizontal, in degrees counterclockwise axes: major and minor axes (order not guaranteed) area: ellipse area points: a series of points on the ellipse, for plotting ''' if len(args)==1: xy = np.array(args[0]) xy = xy.reshape(-1,2) x = xy[:,0:1] y = xy[:,1:] elif len(args)==2: x = args[0] y = args[1] #Require at least 6 points# if x.shape[0]<self.min_points: raise np.linalg.LinAlgError(f"Insufficient data for fitting ellipse, {self.min_points} required, received {x.shape[0]}") #The general form of a conic is Ax**2 + By**2 + Cx + Dy + E == 0 D=np.hstack([x*x,x*y,y*y,x,y,np.ones(x.shape)]) #But we can't just least-squares solve this matrix equation, because #we could end up with any conic! We need to constrain to the ellipse #case, ie we have a constrained minimization problem. #The algorithm to do this is from here: #https://github.com/ndvanforeest/fit_ellipse/blob/master/fitEllipse.pdf #With additional adaptions from here: #https://stackoverflow.com/a/48002645/12488760 S=np.dot(D.T,D) C=np.zeros([6,6]) C[0,2]=C[2,0]=2 C[1,1]=-1 E,V=np.linalg.eig(np.dot(np.linalg.inv(S),C)) n=np.argmax(E) a=V[:,n] b,c,d,f,g,a=a[1]/2., a[2], a[3]/2., a[4]/2., a[5], a[0] num=b*b-a*c if num==0: raise np.linalg.LinAlgError("Insufficient data to fit an ellipse") #Now we know the general form coefficients, so we can convert them #to useful quantities self.centre_x = (c*d-b*f)/num self.centre_y = (a*f-b*d)/num self.centre = (self.centre_x,self.centre_y) self.angle = 0.5*np.arctan(2*b/(a-c))*180/np.pi #In degrees up = 2*(a*f*f+c*d*d+g*b*b-2*b*d*f-a*c*g) down1=(b*b-a*c)*( (c-a)*np.sqrt(1+4*b*b/((a-c)*(a-c)))-(c+a)) down2=(b*b-a*c)*( (a-c)*np.sqrt(1+4*b*b/((a-c)*(a-c)))-(c+a)) radius_horizontal=np.sqrt(abs(up/down1)) radius_vertical =np.sqrt(abs(up/down2)) self.axes = (radius_horizontal,radius_vertical) self.area = np.pi*radius_horizontal*radius_vertical ell=Ellipse(self.centre, radius_horizontal*2., radius_vertical*2., self.angle) self.points=ell.get_verts() self.sum_of_squares_error = self.get_mean_squared_error(xy)