Dear Researchers,
I wrote two function to fit a circle using noisy data.
1- the fitCircle() is derived from MATLAB code of * zhak Bucher* from the
link
http://www.mathworks.com/matlabcentral/fileexchange/5557-circle-fit/content/circfit.m
2- the CircleFitByPratt() from MATLAB code of *Nikolai Chernov *from the
link
http://www.mathworks.com/matlabcentral/fileexchange/22643-circle-fit-pratt-method/content/CircleFitByPratt.m,
based on:
*V. Pratt, "Direct least-squares fitting of algebraic surfaces",
Computer
Graphics, Vol. 21, pages 145-152 (1987)*
I am looking for new methods to compare and improve my analysis because
the error increase with decreasing of points used in the functions.
Thanks for all suggestions
Gianni
Here the funtions with example
# fitCircle, returns:
# xf,yf = centre of the fitted circle
# Rf = radius of the fitted circle
# Cf = circumference of the fitted circle
# Af = Area of the fitted circle
fitCircle <- function(x,y){
a = qr.solve(cbind(x,y,rep(1,length(x))),cbind(-(x^2+y^2)))
xf = -.5*a[1]
yf = -.5*a[2]
Rf = sqrt((a[1]^2+a[2]^2)/4-a[3])
Cf = 2*pi*Rf
Af = pi*(Rf^2)
m <- cbind(xf,yf,Rf,Cf,Af)
return(m)}
# CircleFitByPratt, returns:
# [,1] and [,2] = centre of the fitted circle
# [,3] = radius of fitted cirlce
CircleFitByPratt <- function(x,y){
n <- length(x)
centroid <- cbind(mean(x),mean(y))
Mxx=0; Myy=0; Mxy=0; Mxz=0; Myz=0; Mzz=0;
for(i in 1:n){
Xi <- x[[i]] - centroid[1]
Yi <- y[[i]] - centroid[2]
Zi <- (Xi*Xi) + (Yi*Yi)
Mxy = Mxy + Xi*Yi;
Mxx = Mxx + Xi*Xi;
Myy = Myy + Yi*Yi;
Mxz = Mxz + Xi*Zi;
Myz = Myz + Yi*Zi;
Mzz = Mzz + Zi*Zi;
}
Mxx = Mxx/n
Myy = Myy/n
Mxy = Mxy/n
Mxz = Mxz/n
Myz = Myz/n
Mzz = Mzz/n
# computing the coefficients of the characteristic polynomial
Mz = Mxx + Myy;
Cov_xy = Mxx*Myy - Mxy*Mxy;
Mxz2 = Mxz*Mxz;
Myz2 = Myz*Myz;
A2 = 4*Cov_xy - 3*Mz*Mz - Mzz;
A1 = Mzz*Mz + 4*Cov_xy*Mz - Mxz2 - Myz2 - Mz*Mz*Mz;
A0 = Mxz2*Myy + Myz2*Mxx - Mzz*Cov_xy - 2*Mxz*Myz*Mxy + Mz*Mz*Cov_xy;
A22 = A2 + A2;
epsilon=1e-12;
ynew=1e+20;
IterMax=20;
xnew = 0;
# Newton's method starting at x=0
epsilon=1e-12;
ynew=1e+20;
IterMax=20;
xnew = 0;
iter=1:IterMax
for (i in 1:IterMax){
yold = ynew;
ynew = A0 + xnew*(A1 + xnew*(A2 + 4.*xnew*xnew));
if (abs(ynew) > abs(yold)){
print('Newton-Pratt goes wrong direction: |ynew| >
|yold|')
xnew = 0;
break
}
Dy = A1 + xnew*(A22 + 16*xnew*xnew);
xold = xnew;
xnew = xold - ynew/Dy;
if (abs((xnew-xold)/xnew) < epsilon) {break}
if(iter[[i]] >= IterMax){
print('Newton-Pratt will not converge');
xnew = 0;
}
if(xnew < 0.){
print('Newton-Pratt negative root: x=',xnew);
}
}
DET = xnew*xnew - xnew*Mz + Cov_xy;
Center = cbind(Mxz*(Myy-xnew)-Myz*Mxy , Myz*(Mxx-xnew)-Mxz*Mxy)/DET/2;
# computing the circle parameters
DET = xnew*xnew - xnew*Mz + Cov_xy;
Center = cbind(Mxz*(Myy-xnew)-Myz*Mxy , Myz*(Mxx-xnew)-Mxz*Mxy)/DET/2;
Par = cbind(Center+centroid , sqrt(Center[2]*Center[2]+Mz+2*xnew));
return(Par)
}
#EXAMPLE
library(plotrix)
# Create a Circle of radius=10, centre=5,5
R = 10; x_c = 5; y_c = 5;
thetas = seq(0,pi,(pi/64));
xs = x_c + R*cos(thetas)
ys = y_c + R*sin(thetas)
# Now add some random noise
mult = 0.5;
xs = xs+mult*rnorm(rnorm(xs));
ys = ys+mult*rnorm(rnorm(ys));
plot(xs,ys,pch=19,cex=0.5,col="red",xlim=c(-10,20),ylim=c(-10,20),asp=1)
# real circle
draw.circle(x_c,y_c,radius=10,border="black")
points(x_c,y_c,,pch=4,col="black")
CPrat <- CircleFitByPratt(xs,ys)
draw.circle(CPrat[1],CPrat[2],radius=CPrat[3],border="blue")
points(CPrat[1],CPrat[2],pch=4,col="blue")
MyC <- fitCircle(xs,ys)
draw.circle(MyC[1],MyC[2],radius=MyC[3],border="green")
points(MyC[1],MyC[2],pch=4,col="green")
# Select less points
points(xs[20:49],ys[20:49])
MyC1 <- fitCircle(xs[20:49],ys[20:49])
draw.circle(MyC1[1],MyC1[2],radius=MyC1[3],border="blue",lty=2,lwd=2)
CPrat1 <- CircleFitByPratt(xs[20:49],ys[20:49])
draw.circle(CPrat1[1],CPrat1[2],radius=CPrat1[3],border="green",lty=2,lwd=2)
points(CPrat[1],CPrat[2],pch=4,col="red")
[[alternative HTML version deleted]]