Skip to content

Commit 8acf4e8

Browse files
committed
rectangle fitting
1 parent 1e79730 commit 8acf4e8

File tree

1 file changed

+138
-0
lines changed

1 file changed

+138
-0
lines changed
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
"""
2+
3+
Object shape recognition with rectangle fitting
4+
5+
author: Atsushi Sakai (@Atsushi_twi)
6+
7+
"""
8+
9+
import matplotlib.pyplot as plt
10+
import math
11+
import random
12+
import numpy as np
13+
14+
show_animation = True
15+
16+
17+
def circle_fitting(x, y):
18+
"""
19+
Circle Fitting with least squared
20+
input: point x-y positions
21+
output cxe x center position
22+
cye y center position
23+
re radius of circle
24+
error: prediction error
25+
"""
26+
27+
sumx = sum(x)
28+
sumy = sum(y)
29+
sumx2 = sum([ix ** 2 for ix in x])
30+
sumy2 = sum([iy ** 2 for iy in y])
31+
sumxy = sum([ix * iy for (ix, iy) in zip(x, y)])
32+
33+
F = np.array([[sumx2, sumxy, sumx],
34+
[sumxy, sumy2, sumy],
35+
[sumx, sumy, len(x)]])
36+
37+
G = np.array([[-sum([ix ** 3 + ix * iy ** 2 for (ix, iy) in zip(x, y)])],
38+
[-sum([ix ** 2 * iy + iy ** 3 for (ix, iy) in zip(x, y)])],
39+
[-sum([ix ** 2 + iy ** 2 for (ix, iy) in zip(x, y)])]])
40+
41+
T = np.linalg.inv(F).dot(G)
42+
43+
cxe = float(T[0] / -2)
44+
cye = float(T[1] / -2)
45+
re = math.sqrt(cxe**2 + cye**2 - T[2])
46+
47+
error = sum([np.hypot(cxe - ix, cye - iy) - re for (ix, iy) in zip(x, y)])
48+
49+
return (cxe, cye, re, error)
50+
51+
52+
def get_sample_points(cx, cy, cr, angle_reso):
53+
x, y, angle, r = [], [], [], []
54+
55+
# points sampling
56+
for theta in np.arange(0.0, 2.0 * math.pi, angle_reso):
57+
nx = cx + cr * math.cos(theta)
58+
ny = cy + cr * math.sin(theta)
59+
nangle = math.atan2(ny, nx)
60+
nr = math.hypot(nx, ny) * random.uniform(0.95, 1.05)
61+
62+
x.append(nx)
63+
y.append(ny)
64+
angle.append(nangle)
65+
r.append(nr)
66+
67+
# ray casting filter
68+
rx, ry = ray_casting_filter(x, y, angle, r, angle_reso)
69+
70+
return rx, ry
71+
72+
73+
def ray_casting_filter(xl, yl, thetal, rangel, angle_reso):
74+
rx, ry = [], []
75+
rangedb = [float("inf") for _ in range(
76+
int(math.floor((math.pi * 2.0) / angle_reso)) + 1)]
77+
78+
for i in range(len(thetal)):
79+
angleid = math.floor(thetal[i] / angle_reso)
80+
81+
if rangedb[angleid] > rangel[i]:
82+
rangedb[angleid] = rangel[i]
83+
84+
for i in range(len(rangedb)):
85+
t = i * angle_reso
86+
if rangedb[i] != float("inf"):
87+
rx.append(rangedb[i] * math.cos(t))
88+
ry.append(rangedb[i] * math.sin(t))
89+
90+
return rx, ry
91+
92+
93+
def plot_circle(x, y, size, color="-b"):
94+
deg = list(range(0, 360, 5))
95+
deg.append(0)
96+
xl = [x + size * math.cos(math.radians(d)) for d in deg]
97+
yl = [y + size * math.sin(math.radians(d)) for d in deg]
98+
plt.plot(xl, yl, color)
99+
100+
101+
def main():
102+
103+
# simulation parameters
104+
simtime = 15.0 # simulation time
105+
dt = 1.0 # time tick
106+
107+
cx = -2.0 # initial x position of obstacle
108+
cy = -8.0 # initial y position of obstacle
109+
cr = 1.0 # obstacle radious
110+
theta = math.radians(30.0) # obstacle moving direction
111+
angle_reso = math.radians(3.0) # sensor angle resolution
112+
113+
time = 0.0
114+
while time <= simtime:
115+
time += dt
116+
117+
cx += math.cos(theta)
118+
cy += math.cos(theta)
119+
120+
x, y = get_sample_points(cx, cy, cr, angle_reso)
121+
122+
ex, ey, er, error = circle_fitting(x, y)
123+
print("Error:", error)
124+
125+
if show_animation:
126+
plt.cla()
127+
plt.axis("equal")
128+
plt.plot(0.0, 0.0, "*r")
129+
plot_circle(cx, cy, cr)
130+
plt.plot(x, y, "xr")
131+
plot_circle(ex, ey, er, "-r")
132+
plt.pause(dt)
133+
134+
print("Done")
135+
136+
137+
if __name__ == '__main__':
138+
main()

0 commit comments

Comments
 (0)