[Tutorial OpenCV] “Ball Tracker” con Filtro di Kalman

Kalman Tracking and OpenCV
Kalman Tracking and OpenCV
Kalman Tracking and OpenCV

Tempo fa ho pubblicato su YouTube un video di un “semplice” software in grado di identificare una pallina blu su un tavolo e di inseguirne i movimenti stimando la sua posizione anche in caso di occlusione. Il video, riproposto qui di seguito, ha riscontrato molto successo e ha superato le 5000 visite. In tantissimi mi hanno chiesto un tutorial o il codice sorgente… il codice sorgente ha girato il mondo, ora è arrivato il momento di scrivere il tutorial.

Il codice sorgente che ho utilizzato per realizzare il video è il seguente:

Il codice è calibrato in modo da riconoscere e inseguire una pallina blu. Potete modificare il colore semplicemente cambiando i valori di tinta (HUE) “MIN_H_BLUE” and “MAX_H_BLUE” all’inizio del codice sorgente. Sebbene il codice è molto breve, la teoria che vi è nascosta è molto complicata è richiede delle conoscenze matematiche di alto livello. Il tracking della pallina blu sfrutta quello che è noto in letteratura come “Filtro di Kalman“, che è uno “Stimatore asintotico dello stato del sistema”, cioè è uno strumento matematico che permette di stimare la futura posizione dell’oggetto che stiamo “inseguendo”, utilizzando il modello cinematico dell’oggetto e la sua “storia”. La pagina di Wikipedia dedicata al Filtro di Kalman ne da una spiegazione molto completa, anche se non molto facile da comprendere. Semplicemente si può dire che un filtro di Kalman è realizzato in due fasi: STIMA (o PREDIZIONE) e AGGIORNAMENTO. La fase di STIMA permette di predirre la posizione dell’oggetto inseguito conoscendone la storia, cioè la velocità con cui si è mosso in passato e conoscendo le equazioni che ne controllano il moto. La stima è corretta ogni qualvolta che è disponibile una misura dello stato del sistema (posizione, velocità, ecc), questa correzione realizza la fase di AGGIORNAMENTO. Purtroppo la misura dello stato non è “certa”, ogni misura si porta dietro degli errori, per questo le due fasi di STIMA e AGGIORNAMENTO sono utilizzate in modo pesato considerando anche le informazioni sull’errore di misura e l’errore di predizione. Dopo questo breve tentativo di descrivere in “parole semplici” il Filtro di Kalman, possiamo passare alla spiegazione del codice. Innanzitutto definiamo lo stato del nostro sistema, cioè le informazioni relative alla “pallina” ad ogni istante “t“. Lo stato è definito dal vettore [x,y,v_x,v_y,w,h] (riga 45):

  • x, y: coordinate del baricentro della pallina all’interno dell’immagine
  • v_x, v_y: velocità di spostamento del baricentro (pixel/sec)
  • w, h: larghezza e altezza del bounding box della pallina

Definito il “vettore di stato” è necessario definire il vettore della “misura” [z_x,z_y,z_w,z_h] (riga 46):

  • z_x, z_y: coordinate del baricentro della pallina INDIVIDUATA nell’immagine corrente
  • z_w, z_h: dimensioni del bounding box della pallina INDIVIDUATA nell’immagine corrente

Una nota: il vettore della “misura” è utilizzato nella fase di AGGIORNAMENTO, fase che può essere realizzata solo se la pallina è individuata nell’immagine corrente, altrimenti (caso di OCCLUSIONE) si procede con la sola STIMA della sua posizione e dimensione. Nelle righe da 50 a 88 sono definite le MATRICI che implementano le fasi di misura e aggiornamento dello stato. La matrice A implementa il modello “lineare” del movimento della pallina. Per questo esempio è stata fatta l’assunzione FORTE che la pallina si muove in modo lineare, cosa purtroppo non vera per la maggior parte dei sistemi dinamici più complicati. La matrice H implementa la misura. La matrice Q è la matrice di “covarianza del processo“, un nome altisonante per identificare il “rumore” del sistema, cioè la stima dell’errore sullo stato della pallina ad ogni suo istante. Impostati i parametri del Filtro di Kalman possiamo avviare la nostra webcam e iniziare a tracciare i movimenti della pallina nel ciclo “while”. La variabile “dT” contiene la misura del tempo intercorso tra una fase e la successiva, tempo fondamentale per la predizione dello stato. Se la pallina è stata “identificata” in qualche istante precedente (found è TRUE e il “tracking” è in esecuzione, dalla riga 132 alla riga 156 realizziamo la fase di STIMA, utilizzando la funzione OpenCV “predict“. Dalla riga 158 alla riga 208 è eseguita la “classica” procedura di identificazione di un oggetto colorato: blurring, conversione HSV, filtraggio morfologico, thresholding e filtraggio per dimensioni e area. Dalla riga 231 alla riga 277 è eseguita la fase di AGGIORNAMENTO dello stato della pallina con le misure effettuate durante la fase di identificazione in particolare dalla riga 162 alla riga 266 la posizione e la dimensione iniziali sono impostate con quelle misurate la prima volta che la pallina è vista nel flusso video (found è FALSE). La variabile “found” indica che una pallina è stata precedentemente identificata e il tracking è in esecuzione. Se la pallina viene “persa di vista” per 10 frame consecutivi “found” diventa FALSE e il processo di tracking viene interrotto per essere ripreso ad una successiva identificazione. Nelle righe da 212 a 229 il risultato del tracking è messo a schermo. Il rettangolo verde mostra il bounding box della pallina “identificata” nella fase di misura. Il rettangolo rosso mostra invece lo STATO STIMATO dal filtro di Kalman. Interessante notare come lo stato della pallina coincida con lo stato reale fin quando la pallina si muove in “modo lineare”, ma soprattutto è interessante notare che la stima della posizione della pallina rimane valida anche quando la pallina scompare dietro la bottiglia e ricompare successivamente sull’altro lato. Il tutorial è concluso, OpenCV mette a disposizione di tutti un potente strumento matematico di non facile comprensione e implementazione quale è il Filtro di Kalman. Spero che questa breve guida possa aiutarvi nel capire come utilizzarlo nei vostri progetti. Per qualsiasi domanda potete scrivermi al mio solito indirizzo email: myzhar@robot-home.it

Il codice sorgente è disponibile su Github:

64 Responses

  1. Sanbhu says:

    This tutorial is awesome. Thank you very much for such a nice tutorial. I am new in this field of tracking. I wanted to work on multiple target tracking problem using Kalman filter. I have been successfully able to track a single pedestrian from a video of multiple pedestrian. But I was not able to solve the problem for multiple pedestrian tracking problem. I know I should initialize multiple trackers for them. But I don’t know how to do that. Can you please give me some feedback. Many many thanks in advance.

    • Myzhar says:

      Hi Sanbhu… it is not easy to perform multi-tracking. In theory it is really simple: every time you identify a new “object” to track, you create a new Kalman Filter for that object and you start a new tracker.
      The problems are two:
      1) understand when a moving object is new
      2) associate the right Kalman Filter to the right moving object frame by frame… managing occlusions too!
      There are a lot of techniques to solve this problem and I cannot speak about them in a blog comment… you must search for the good papers on Google.
      The keyword to use are “Mahalanobis distance”, “Covariance Matrix” to weight the Mahalanobis distance, object recognition frame by frame.
      There is also a new algorithm in OpenCV 3.0, it’s an implementation of this: http://personal.ee.surrey.ac.uk/Personal/Z.Kalal/tld.html
      You can write me an email to myzhar@robot-home.it if you need more help.

  2. Ciobotaru Costel says:

    This filter can be used for tracking a 3d object?

  3. janan says:

    Can i use this for eye gaze to know the eye direction ??

    • Myzhar says:

      Sure. You must estimate the status of the pupil and calculate the direction as atan2 (vy,vx), where vy and vx are the speed in pixel of the pupil.
      The only doubt I have is about the speed of the pupil. Kalman Filter is good for object that do not change their speed fast… a pupil has really big accelerations as far as I know…

  4. ufoRobot says:

    Thank you very much for the tutorial. I would like to ask you a question: why did you initialize cv::setIdentity(kf.measurementNoiseCov with this value cv::Scalar(1e-1)
    and also for example kf.processNoiseCov.at(0) with this one 1e-2 ? How did you find that values ?
    thanks in advance

    • Myzhar says:

      Well… this is the difficult of the Kalman Filter. Understand the better starting values for the covariances. If you know the error of your sensors you can use them… in this case only testing and trying and testing again

  5. Fabio says:

    Very nice tutorial. I would like to ask you why kf.errorCovPre is just initialized after detection?

  6. Prasanna says:

    Can I use this model to detect a tennis ball? I mean is it fast enough to detect a tennis ball while it is in flight mode?

    • Myzhar says:

      A Kalman filter is surely necessary to track a tennis ball, but the problem is not the speed of the model… you need a very fast camera else you will get a very blurred ball

  7. Bindu says:

    can i use this model to track hand movements..(I mean to find the trajectory of hand)

    • Myzhar says:

      Yes. You must change the color to be tracked. Modify MIN_H_BLUE and MAX_H_BLUE with the Hue values correct to segment the skin

      • Bindu says:

        Actually in my code i have a hand shape video in which i already detected hand the fingers. i just want to track finger movement only…is it possible????

        • Myzhar says:

          Yes… but you must create one Kalman Filter for each finger track. You must adapt this code (that is really simplified) to your detection algorithm

          • Bindu says:

            I am very new in tracking field. If u could help to provide me some code related to my tracking. Thanx for helping me.

          • Myzhar says:

            I’m sorry but it’s not easy to write this kind of code for an application not simple like finger tracking. It is a project that requires hours/days of development/test and it cannot be discussed as a comment of a really simple demo.

  8. Loknath says:

    Not compiling in VS2012,,,getting numerous errors

  9. Ryan says:

    Also, I found the green shows the detection result, and the red shows the predicted value, but if I need the final updated state value, I need to use the value of state after kf.correct(meas) at line 206, am I right?? Thanks for you demo!!

    • Myzhar says:

      Yes. That’s correct. But what happens when you do not have a measure? You have not a state value. This is why I prefer always to keep the “predicted state” as the current value for my system.

  10. Ryan says:

    I found the unit of dT in your code is second, but when I run it, the value of dT is very large, i.e. 703048, is it wrong?? I changed dT to 1, the code still gives right prediction. I found in your code, although your state =[x,y,vx,vy, w,h], but you initialize the vx=0,vy=0, in while loop, I found the value of vx, vy are always zero, can this explain the value of dT doesn’t matter??Thanks.

    • Myzhar says:

      You are right, dT is in seconds, and this is correct. Kalman filters estimates speeds during the prediction step and you must use correct measurements units… so seconds is right.
      I do not know why you are getting values so large for dT. A breakpoint?

  11. Omar says:

    Hi Myzhar
    Thanks for the tutorial.
    Which version of OpenCV did you use? I am using 3.0 and I am having errors!

  12. Jun says:

    Hi, can I know how do I import this to android studios?

  13. adramelch says:

    Not compiling in gcc 5 and opencv 3.1. Mostly vectors missing type (how did you compile vectors without a , because I’ thought it to be impossible? For example vector balls?

    • Myzhar says:

      The tutorial has been written a few time ago when OpenCV 3 did not exist. To use it with OpenCV 3 you must follow the OpenCV guidelines to migrate from OpenCV 2 to OpenCV 3

  14. Hi says:

    I think you forgot this line in initialization, just after you’ve set the state:
    kf.statePost = state;

  15. Hi says:

    Just after:

    state.at(0) = meas.at(0);
    state.at(1) = meas.at(1);
    state.at(2) = 0;
    state.at(3) = 0;
    state.at(4) = meas.at(2);
    state.at(5) = meas.at(3);

  16. Hi says:

    btw if you are tracking multiple points of a same physical object, say the corners of a box, do you know if one must define the correlation metrics of the R and Q matrices? And if so, how to calculate them?

  17. Hi says:

    No, I set up the measurement matrix to include spatial and velocity coordinates for each of the 4 corners, hence at 16 * 16 state matrix

  18. gino says:

    hi
    your code is really strange,the vector and the at function of the mat didn’t follow with type.
    I just expect the

    kf.measurementMatrix.at(0)=1.0f;

    should be

    kf.measurementMatrix.at(0)=1.0f;

    and the

    vector contours;

    should be

    vector contours;

  19. Konyo says:

    Would this tracking method be useful for 3d ball tracking? I want to get the direction and speed of a soccer ball using my Kinect v2…

  20. Konyo says:

    What can I do to improve ball detection? I’ve changed the lower/upper HSV bounds of the ball, but I still get noise. Is there anything else I can change in order to get it more precise? I only have 1 ball. Maybe because you are looking for multiple balls?

  21. c says:

    Hi Myzhar,

    Congratulations, very nice tutorial.

    That said, your computation of the timebase dT seems a bit odd to me. Since you calculate dT inside your main loop (line 97), this would mean that the duration of one iteration of that loop has a direct influence on your timebase and hence the transition matrix A.

    If for instance you were to use a haar cascade for object detection, execution times for one iteration could change a lot. This would add randomness to dT and hence also to Vx and Vy, since:
    Vx(k) = x(k-1) * dT + Vx(k-1)
    Vy(k) = y(k-1) * dT + Vy(k-1)

    Instead assuming the FPS rate of the camera being constant, we could use dT = 1 / FPS as time between two measurements ( frames).

    Am I missing the point? I would be very curious to hear your thoughts on this.

    Best,
    c

    • Myzhar says:

      Hi C,
      your are right! This is a really naive example. The better way to work is to create a thread for the Kalman Filter that runs at almost fixed frequency. You continuously predict the state and you update it only when a new measure is available.

  22. chris says:

    That would be my recommended approach too 🙂

    Another thing that intrigued me: where are the different factors for Ev_x = 2.0 and Ev_y = 1.0 are coming from:
    kf.processNoiseCov.at(14) = 2.0f;
    kf.processNoiseCov.at(21) = 1.0f;

    Is this to better model the movement of the ball on the table (more movement in x-axis then in y-axis)?

    And a last question: if you had to track an object where width and height of the detected object (and hence also the aspect ratio) constantly change due to scale and angle changes, would you still recommend to include w and h in the state vector? Since the aspect ratio is constantly changing, I am not sure if this wouldn’t introduce non-linear components to the model and making it impossible to predict with a Kalman filter.

    Again, very fascinating tutorial! 🙂

    • Myzhar says:

      The values are very experimental. The error on X is double respect to the one on Y because the ball is moving the most horizontally so the filter must be more reactive on X
      About width and height constantly changing I agree with you. With a simple Kalman Filter is a non sense keeping them in the state if the system is highly non linear.
      I approached a similar problem taking the code from the UKF filter from OpenCV 3.1 and modifying it to be used with OpenCV 2.4.13 (opencv4tegra on Nvidia Jetson TK1). With UKF you must not take care about non linearity… the theory behind UKF is really fascinating

  23. Manasa Navalgund says:

    Is there any way to accelerate the process using gpu?

  24. i am new to opencv i need this code but where to run this code in opencv …i have downloaded open cv but where to run this code????

  1. 21/02/2017

    […] “Ball Tracker” con Filtro di Kalman […]

Lascia un Commento

This site uses cookies. Find out more about this site’s cookies.
%d bloggers like this: