Skip to content

Instantly share code, notes, and snippets.

@cshimmin
Created June 23, 2016 15:30
Show Gist options
  • Save cshimmin/90b7e2e9783ae96cbccd1768bede39e7 to your computer and use it in GitHub Desktop.
Save cshimmin/90b7e2e9783ae96cbccd1768bede39e7 to your computer and use it in GitHub Desktop.
sketch of hough transform based track finding/fitting
#!/usr/bin/env python
import ROOT as r
import sys
import itertools as it
import numpy as np
import pylab as pl
from scipy.special import cotdg
from scipy.sparse import dok_matrix
def hough_radial(points, radius=5):
points = points.copy()
points[:,0] -= 0.5
points[:,1] -= 0.5
xmax = int(points[:,0].max())+1
ymax = int(points[:,1].max())+1
img = dok_matrix((xmax, ymax), dtype=int)
for px,py,pv in points:
img[px,py] = pv
theta_values = np.linspace(-np.pi,np.pi,nbins_theta)
ct = np.cos(theta_values)
st = np.sin(theta_values)
'''
for x0,y0 in img.keys():
chunk = img[max(0,x0-radius):x0+radius,max(0,y0-radius):y0+radius]
for px,py in chunk.keys():
rvals = (x0+(px-chunk.shape[0]/2)*ct + (y0+(py-chunk.shape[0]/2)*st
'''
return img
def hough_lines(points, nmin=3):
nbins_theta = 180
theta_values = np.linspace(-np.pi,np.pi,nbins_theta)
print theta_values.min(), theta_values.max()
hits = []
pl.figure()
ct = np.cos(theta_values)
st = np.sin(theta_values)
for p in points:
x,y,v = p
#shifts = np.random.normal(loc=0, scale=0.5, size=(100,2))
shifts = [(0,0)]
for dx, dy in shifts:
rvals = (x+dx)*ct + (y+dy)*st
tvals = theta_values[rvals>0]
rvals = rvals[rvals>0]
pl.plot(tvals, rvals)
if len(rvals):
hits.extend(list(zip(rvals, tvals)))
hits = np.array(hits)
rmax = int(np.ceil(hits[:,0].max()))
if rmax%2==1: rmax+=1
h2d = np.histogram2d(hits[:,0], hits[:,1], range=[[0,rmax],[-np.pi,np.pi]], bins=[rmax*10,nbins_theta])
#print h2d[0]
print "avg: %.2f, max: %.2f" % (h2d[0].mean(), h2d[0].max())
#print h2d[0].max()
print "nmax: ", np.sum(h2d[0]==h2d[0].max())
print "nover: ", np.sum(h2d[0]>=nmin)
#pl.matshow(h2d[0])
argmax = np.unravel_index(h2d[0].argmax(), h2d[0].shape)
r0 = h2d[1][argmax[0]]
theta0 = h2d[2][argmax[1]]
pl.plot([theta0], [r0], 'rx', markersize=20)
pl.yscale('log')
pl.ylim(ymin=3e2)
pl.figure()
pl.hexbin(hits[:,0], hits[:,1])
nover = 4 #np.sum(h2d[0]>=nmin)
candidates = []
best_candidate = None
minc2 = np.inf
for i in xrange(nover):
loc = np.unravel_index(h2d[0].argmax(), h2d[0].shape)
h2d[0][loc]=-1
r0 = h2d[1][loc[0]]
theta0 = h2d[2][loc[1]]
candidates.append((r0,theta0))
c2 = chi2(points, r0, theta0).sum()
if c2 < minc2:
best_candidate = (r0, theta0)
minc2 = c2
pl.plot([r0], [theta0], 'rx', markersize=20)
print "count at argmax: ", h2d[0][argmax]
#print r0, theta0
print 'note: rmax =', rmax
print "next biggest:", h2d[0][h2d[0]!=h2d[0].max()].max()
#return r0, theta0
return best_candidate
def ftrack(points, minpoints=3, minchi2=3):
points = points.copy()
while len(points)>minpoints:
line = hough_lines(points)
r0, theta0 = line
c2 = chi2(points, r0, theta0)
if c2.sum() > minchi2*len(points):
print "rejecting %d points with (r,theta) = (%.2f, %.2f); red. chi2 = %.2f" % (len(points), r0, theta0, c2.sum()/len(points))
print "(m=%.2f, b=%.2f)" % trig_to_rect(r0,theta0)
points = points[c2!=c2.max()]
line = None
else:
print "accepting %d points with (r,theta) = (%.2f, %.2f); red. chi2 = %.2f" % (len(points), r0, theta0, c2.sum()/len(points))
print "(m=%.2f, b=%.2f)" % trig_to_rect(r0,theta0)
break
if line is None: return
return r0, theta0, points
def hough(points):
vertices = []
for p1,p2 in it.combinations(points, r=2):
x1,y1,v1 = p1
x2,y2,v1 = p2
if y1==y2:
theta = 0
else:
theta = np.arctan( 1.*(x1-x2) / (y2-y1) )
r = x1*np.cos(theta) + y1*np.sin(theta)
if r<0:
r = -r
if theta>0: theta-=np.pi
else: theta += np.pi
vertices.append((r,theta))
#print "Got %d vertices from %d points." % (len(vertices), len(points))
return np.array(vertices)
def hough_rect(points):
vertices = []
for p1,p2 in it.combinations(points, r=2):
x1,y1 = p1
x2,y2 = p2
if x1==x2: continue
m = 1.*(y2-y1) / (x2-x1)
b = y1 - m*x1
vertices.append((m,b))
#print "Got %d vertices from %d points." % (len(vertices), len(points))
return np.array(vertices)
def find_vertex(vtxs, points):
minc2 = np.inf
best_vtx = None
for v in vtxs:
c2 = chi2(points, v[0], v[1]).sum()
if c2<minc2:
minc2 = c2
best_vtx = v
return best_vtx
#r0 = vtxs[:,0].mean()
#theta0 = vtxs[:,1].mean()
#return r0, theta0
def find_vertex_rect(vtxs, median=False):
if len(vtxs)==0: return
if median:
m0 = np.median(vtxs[:,0])
b0 = np.median(vtxs[:,1])
return m0, b0
m0 = vtxs[:,0].mean()
b0 = vtxs[:,1].mean()
return m0, b0
def trig_to_rect(r, theta):
#if np.allclose(theta,0):
if theta == 0:
m = np.inf
b = np.inf
else:
m = -1./np.tan(theta)
b = r/np.sin(theta)
return m,b
def chi2(points, r, theta):
#if np.allclose(theta, 0):
if theta == 0:
# vertical line, just use xvalues
dist = (points[:,0] - r)
else:
# convert to rectilinear and then
# use perpendicular distance to line:
m,b = trig_to_rect(r, theta)
dist = (m*points[:,0] - points[:,1] + b) / np.sqrt(m**2 + 1)
#weights = 0.5/points[:,2]
weights = 0.5
return (dist/weights)**2
def chi2_rect(points, m, b):
# use perpendicular distance to line:
dist = (m*points[:,0] - points[:,1] + b) / np.sqrt(m**2 + 1)
return (dist/0.5)**2
#return (points[:,1] - (points[:,0]*m+b))**2
def fit_track(points, maxiter=-1, minpoints=2, minchi2=3, draw=False):
points = points.copy()
i = 0
line = None
while len(points)>=minpoints:
i += 1
if maxiter>0 and i>maxiter: break
vtxs = hough(points)
rvals = vtxs[:,0]
tvals = vtxs[:,1]
#wvals = vtxs[:,2]
rmax = int(np.ceil(rvals.max()))
if rmax <= 0:
print "Warning! Rmax is not positive:", rmax
if rmax%2==1: rmax += 1
h2d = np.histogram2d(rvals,tvals,range=[[0,rmax],[-np.pi,np.pi]],bins=[rmax*2,360])
argmax = np.unravel_index(h2d[0].argmax(), h2d[0].shape)
r0 = h2d[1][argmax[0]]
theta0 = h2d[2][argmax[1]]
line = r0,theta0
if draw:
pl.figure(0)
pl.clf()
pl.matshow(h2d[0], fignum=0)
if draw:
pl.figure(i)
#pl.scatter(vtxs[:,0], vtxs[:,1])
pl.hexbin(vtxs[:,0], vtxs[:,1])
pl.xlabel("r")
pl.ylabel("theta")
#line = find_vertex(vtxs, points)
if line is None: return
r0, theta0 = line
if draw:
pl.plot([r0], [theta0], 'r+', markersize=20)
pl.figure(1)
pl.plot([r0], [theta0], '+', markersize=20)
c2 = chi2(points, r0, theta0)
if draw:
pl.figure(i)
pl.title('chi2=%.2f'%(c2.sum() / len(points)))
if c2.sum() > minchi2*len(points):
points = points[c2!=c2.max()]
line = None
else:
#print "Final red. chi2:", c2.sum()/len(points)
break
if line is None:
return
return r0, theta0, points
def fit_track_rect(points, maxiter=-1, minpoints=2, minchi2=3):
points = points.copy()
i = 0
line = None
while len(points)>=minpoints:
i += 1
if maxiter>0 and i>maxiter: break
vtxs = hough_rect(points)
#pl.figure()
#pl.scatter(vtxs[:,0], vtxs[:,1])
#pl.plot([vtxs[:,0].mean()], [vtxs[:,1].mean()], 'r+', markersize=20)
#pl.xlabel("slope")
#pl.ylabel("offset")
line = find_vertex_rect(vtxs)
if not line: return
m0,b0 = line
c2 = chi2_rect(points, m0, b0)
#pl.figure()
#pl.hist(c2)
#pl.xlabel("chi^2")
#pl.ylabel("points")
if c2.sum() > minchi2*len(points):
points = points[c2!=c2.max()]
line = None
else:
break
if not line:
return
return m0, b0, points
def find_tracks(t, maxiter=0, rotate=False, get_points=False, minpoints=3, minchi2=3, draw=False):
if not rotate:
points = np.array([(x+0.5, y+0.5, v/256.) for x,y,v in zip(t.pix_x, t.pix_y, t.pix_val)])
else:
points = np.array([(-y-0.5, x+0.5, v/256.) for x,y,v in zip(t.pix_x, t.pix_y,t.pix_val)])
tracks = []
track_points = []
while len(points) > 3:
result = ftrack(points, minpoints=minpoints, minchi2=minchi2)
if not result: break
r0, theta0, p0 = result
m0, b0 = trig_to_rect(r0, theta0)
if m0 == np.inf:
x0 = p0[:,0].min()
x1 = p0[:,0].max()
y0 = p0[:,1].min()
y1 = p0[:,1].max()
#assert x0==x1
if x0!=x1:
print "Warning! Got infinite slope but delta(X)!=0.", x0,x1,y0,y1
L = y1-y0
else:
x0, x1 = p0[:,0].min(), p0[:,0].max()
y0 = m0*x0 + b0
y1 = m0*x1 + b0
L = np.sqrt( (x1-x0)**2 + (y1-y0)**2 )
theta = np.arctan(m0)
c2 = chi2(p0,r0,theta0)
sum_chi2 = c2.sum()
if np.isnan(sum_chi2):
print "WTF! got nan in chi2"
print "theta0 = ", theta0, "r0 =", r0
tracks.append((x0,y0,x1,y1,L,theta,len(p0),sum_chi2))
if get_points:
track_points.append(p0)
used = np.array([not p in p0 for p in points])
points = points[ used ]
tracks = np.array(tracks, dtype=[('x0',float),('y0',float),('x1',float),('y1',float),('l', float), ('theta', float), ('n', int), ('chi2', float)])
if get_points:
return tracks, track_points
else:
return tracks
def find_tracks2(t, maxiter=0, rotate=False, get_points=False, minpoints=3, minchi2=3, draw=False):
if not rotate:
points = np.array([(x+0.5, y+0.5, v/256.) for x,y,v in zip(t.pix_x, t.pix_y, t.pix_val)])
else:
points = np.array([(-y-0.5, x+0.5, v/256.) for x,y,v in zip(t.pix_x, t.pix_y,t.pix_val)])
tracks = []
track_points = []
i = 0
while len(points) > 2:
i += 1
if maxiter>0 and i>maxiter: break
result = fit_track(points, minpoints=minpoints, minchi2=minchi2, draw=draw)
if result is None: break
r0, theta0, p0 = result
m0, b0 = trig_to_rect(r0, theta0)
if m0 == np.inf:
x0 = p0[:,0].min()
x1 = p0[:,0].max()
y0 = p0[:,1].min()
y1 = p0[:,1].max()
#assert x0==x1
if x0!=x1:
print "Warning! Got infinite slope but delta(X)!=0.", x0,x1,y0,y1
L = y1-y0
else:
x0, x1 = p0[:,0].min(), p0[:,0].max()
y0 = m0*x0 + b0
y1 = m0*x1 + b0
L = np.sqrt( (x1-x0)**2 + (y1-y0)**2 )
theta = np.arctan(m0)
c2 = chi2(p0,r0,theta0)
sum_chi2 = c2.sum()
if np.isnan(sum_chi2):
print "WTF! got nan in chi2"
print "theta0 = ", theta0, "r0 =", r0
tracks.append((x0,y0,x1,y1,L,theta,len(p0),sum_chi2))
if get_points:
track_points.append(p0)
used = np.array([not p in p0 for p in points])
points = points[ used ]
tracks = np.array(tracks, dtype=[('x0',float),('y0',float),('x1',float),('y1',float),('l', float), ('theta', float), ('n', int), ('chi2', float)])
if get_points:
return tracks, track_points
else:
return tracks
def find_tracks_rect(t, maxiter=-1, rotate=False):
if not rotate:
points = np.array([(x+0.5, y+0.5) for x,y in zip(t.pix_x, t.pix_y)])
else:
points = np.array([(-y-0.5, x+0.5) for x,y in zip(t.pix_x, t.pix_y)])
tracks = []
i = 0
while len(points) > 2 and (maxiter>-1 and i<=maxiter):
i += 1
result = fit_track(points)
if not result: break
m0, b0, p0 = result
x0, x1 = p0[:,0].min(), p0[:,0].max()
y0 = m0*x0 + b0
y1 = m0*x1 + b0
L = np.sqrt( (x1-x0)**2 + (y1-y0)**2 )
theta = np.arctan(m0)
tracks.append((x0,y0,x1,y1, L, theta, len(p0), chi2_rect(p0,m0,b0).sum()))
used = np.array([not p in p0 for p in points])
points = points[ used ]
tracks = np.array(tracks, dtype=[('x0',float),('y0',float),('x1',float),('y1',float),('l', float), ('theta', float), ('n', int), ('chi2', float)])
return tracks
if __name__ == "__main__":
infile = sys.argv[1]
t0 = r.TChain("event")
t0.Add(infile)
'''
t1 = t0.CloneTree(0)
t1._pix_hc = r.vector('double')()
t1.Branch("pix_hc", t1._pix_hc)
t1._pix_hd = r.vector('double')()
t1.Branch("pix_hd", t1._pix_hd)
for evt in t0:
t1._pix_hc.clear()
t1._pix_hd.clear()
for px, py in zip(evt.pix_x, evt.pix_y):
d =
'''
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment