larsupilami73

Particle Filter

Dec 27th, 2019
428
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.39 KB | None | 0 0
  1. #!/usr/bin/python3
  2. #
  3. #Demo of a particle filter for object tracking:
  4. #The goal to track is the white square moving about in the image
  5. #Several 'distractors' noise dots are also there to make it harder.
  6. #A 'particle' is a set of coordinates (x,y) with a value attached to it.
  7. #The value is the measure of a probability distribution function (pdf), here of the location of the object.
  8. #The main idea is that the density of the particles represents the pdf itself.
  9. #So where the pdf is high, we want to have more particles.
  10. #To get to this situation, we do the following:
  11. #   -start from a uniform distribution (prior) with some particles at random positions. Each particle is just as likely to be at the objects location: p(x,y)=1/n
  12. #   -measure the actual value of the pdf at the position of the particles.This is done by measuring the number of white pixels around a particle's location.
  13. #   -calculate the importance weight w of each particle
  14. #   -now resample the particles with replacement, according to their weights. Particles with higher weights get sampled more often.
  15. #   -make the position estimate as the centre-of-mass of the particles or (smarter) of the best (highest weighted) particles.
  16. #   -perturb the particles a bit (depending on the problem I guess)
  17. #   -repeat
  18. #
  19. #Stuff to play with:
  20. # -the size and number of noise dots
  21. # -number of particles
  22. # -particle perturbation stepsize and object velocity (must be +- moving-dot-velocity)
  23. # -Idea: make a 2nd order particle filter --> estimate the velocity of the white square too, and make the particles follow
  24.  
  25. #Based loosely on: https://scipy-cookbook.readthedocs.io/items/ParticleFilter.html
  26. #
  27. #larsupilami73
  28.  
  29.  
  30. from scipy import *
  31. import matplotlib.pyplot as plt
  32. import os
  33.  
  34. random.seed(42)
  35.  
  36.  
  37. # parameters
  38. N = 100     #image size
  39. d = 8       #square object size
  40. K = 250     #number of images in the animation
  41. Kn = 15     #number of noise dots
  42. dn = 2      #noise dot size, increase to make it harder to follow the object
  43.  
  44. #this generates a moving square that is to be followed by the particle filter
  45. def imagegen():
  46.     x, y = 80, 20   #position of topleft object corner
  47.     px = +2.9       #initial velocity
  48.     py = -2.7      
  49.     counter = K
  50.     image = zeros((N,N))
  51.     yield image,x,y
  52.    
  53.     while(counter):
  54.         counter-=1
  55.         x = x + px
  56.         y = y + py
  57.         if x>=N-d:
  58.             x = N-d
  59.             px = -px
  60.         elif x<=0:
  61.             x = 0
  62.             px = - px
  63.        
  64.         if y>=N-d:
  65.             y = N-d
  66.             py = -py
  67.         elif y<=0:
  68.             y = 0
  69.             py = -py
  70.        
  71.        
  72.         image = zeros((N,N))
  73.         image[int(x):int(x+d),int(y):int(y+d)] = 1.0 #make pixels white
  74.         #px = px + random.uniform(-0.5,0.5) #perturb the velocity too
  75.         #py = py + random.uniform(-0.5,0.5)
  76.         yield image,x+d//2,y+d//2
  77.    
  78.  
  79. #this is to add random-walking noise to the image,
  80. #to make it more difficult for the particle filter
  81. def noisegen():
  82.     noise = random.randint(low=0,high=N-1,size=(Kn,2))
  83.     pnoise = zeros((Kn,2)) #noise dots velocity
  84.     for k_ in range(Kn): #make sure none have zero velocity
  85.         while(not pnoise[k_].any()):
  86.             pnoise[k_] = random.randint(low=-2,high=3,size=(2))
  87.     noisefield = zeros((N,N))
  88.     for nx,ny in noise:
  89.         noisefield[nx,ny] = 1.0
  90.     yield noisefield
  91.     while(True):
  92.         noise = noise + pnoise
  93.         noise = noise % (ones((Kn,2))*N) #loop the noisedots around the corners of the image
  94.         noise = noise.astype(int)
  95.         noisefield = zeros((N,N))
  96.         for nx,ny in noise:
  97.             noisefield[nx:nx+dn,ny:ny+dn] = 1.0 #make white (1.0) or gray (0< .. <1.0)
  98.         yield noisefield
  99.    
  100.  
  101.  
  102. #a simple particlefilter
  103. class ParticleFilter:
  104.     def __init__(self,numparticles=100,low=0.0,high=1.0):
  105.         self.numparticles = numparticles
  106.         self.particles = random.uniform(low=low,high=high,size=(numparticles,2)) #prior distribution is uniform, each particle is a position (x,y), density of particles needs to reflect the pdf
  107.         self.samples = zeros(numparticles)
  108.         self.weights = ones(numparticles)/numparticles
  109.        
  110.     def update(self, image):
  111.         """
  112.        Update and resample all particles.
  113.        The goal is to have the density of the particles reflect the pdf.
  114.        """
  115.  
  116.         #wiggle the particles a bit, otherwise, we end up with just the same ones after every update
  117.         #the perturbation should be related to the velocity of the object we want to track
  118.         #if the perturbation is too low, compared to the velocity the particles drag behind the object
  119.         #if the perturbation is too high, the cloud of particles is to wide (density does not represent the pdf very well)
  120.         self.particles = self.particles + random.uniform(-4,4,size=self.particles.shape)
  121.         clip(self.particles,0,N)
  122.        
  123.         #measure at the position of the particles
  124.         particles_ = self.particles.astype(int) #must be ints
  125.         for k in range(self.numparticles):
  126.             self.samples[k] = image[particles_[k,1]-d//2:particles_[k,1]+d//2, particles_[k,0]-d//2:particles_[k,0]+d//2].sum() #measure how much of the pixels are 'on' in a square of d x d
  127.        
  128.         #update the importance weights
  129.         self.weights = 1./(1. + (self.samples-d*d)**2)
  130.    
  131.         #normalize importance weights
  132.         self.weights /=self.weights.sum()
  133.        
  134.         #choose particles with replacement, according to their importance weight
  135.         the_choice = random.choice(self.numparticles, size=self.numparticles,replace=True,p=self.weights)
  136.         self.particles = self.particles[the_choice]
  137.         self.weights = self.weights[the_choice]
  138.        
  139.     def estimate(self):
  140.         """
  141.        The estimate of the optimum could be:
  142.        1. the position of the particle with the highest weight
  143.        2. the weighted mean position of all particles
  144.        3. the weighted mean position of the n-most weighty particles
  145.        We go with 3 here.
  146.        """
  147.         numparticles = self.numparticles//4 #25% heaviest particles
  148.         positions_heaviest_particles = self.weights.argsort()[-numparticles:]
  149.         return average(self.particles[positions_heaviest_particles],axis=0,weights=self.weights[positions_heaviest_particles])
  150.        
  151.    
  152.  
  153. #image and noise are generators
  154. noise_g = noisegen()
  155. image_g = imagegen()
  156. #particlefilter is an object that gets fed the images
  157. particlefilter = ParticleFilter(numparticles=100,low=0,high=N)
  158.  
  159.  
  160. #temp directory for the frames
  161. if not os.path.exists('./frames'):
  162.     os.mkdir('./frames')
  163. try:
  164.     os.system('rm ./frames/frame*.png')
  165. except:
  166.     pass
  167.  
  168.  
  169. #make the figure
  170. fig = plt.figure()
  171. ax = fig.add_subplot(111)
  172. ax.set_title('Particle Filter Object Tracking Demo')
  173. image_ax = ax.imshow(random.normal(size=(N,N)), cmap='gray', vmin=0,vmax=1)  #add imshow artist
  174. particle_ax = ax.scatter(particlefilter.particles[:,0],particlefilter.particles[:,1],c='blue',marker='+',alpha=0.7) #add scatter artist
  175. estimate_ax = ax.scatter(0,0,c='red',s=50, marker='o',alpha=0.9)
  176. text_ax = ax.text(1,5,'real pos.: %i,%i' %(0,0),color='lightgreen',zorder=1,fontsize=14) #add text artist
  177. text2_ax = ax.text(1,1,'estimated pos.: %i,%i' %(0,0), color='lightgreen',zorder=1,fontsize=14) #add text artist
  178. ax.set_xlim([0,N-0.5])
  179. ax.set_ylim([0,N])
  180. plt.ion()
  181.  
  182. #here the figure update loop starts
  183. framecounter=0
  184. for (image_,real_x,real_y),noise_ in zip(image_g,noise_g):
  185.    
  186.     #image + noise
  187.     X_ = image_ + noise_
  188.    
  189.     #show the image
  190.     image_ax.set_array(X_)
  191.    
  192.     #update the particlefilter
  193.     particlefilter.update(X_)
  194.    
  195.     #overlay the particles
  196.     particle_ax.set_offsets(particlefilter.particles)
  197.    
  198.     #overlay the estimate of the position of the square
  199.     estimate_y, estimate_x = particlefilter.estimate()
  200.    
  201.     #draw the estimate
  202.     estimate_ax.set_offsets([estimate_y,estimate_x])
  203.    
  204.     #update text
  205.     text_ax.set_text('real pos.: %i,%i' %(real_x,real_y))
  206.     text2_ax.set_text('estimated pos.: %i,%i' %(estimate_x,estimate_y))
  207.    
  208.     plt.draw()
  209.     plt.pause(0.2) #update image every .. seconds
  210.     plt.savefig('./frames/frame%05d.png'%framecounter)
  211.     print('Frame number: %d/%d' %(framecounter,K))
  212.     framecounter +=1
  213.  
  214. plt.close()
  215.  
  216. print('converting to mp4!')
  217. os.system("ffmpeg -y -r 5 -i ./frames/frame%05d.png -c:v libx264 -vf fps=5 particlefilter.mp4")
Advertisement
Add Comment
Please, Sign In to add comment