private double DistantBlobTrack(CvBlob b, CvTrack t) { double d1; if (b.Centroid.X < t.MinX) { if (b.Centroid.Y < t.MinY) { d1 = Math.Max(t.MinX - b.Centroid.X, t.MinY - b.Centroid.Y); } else if (b.Centroid.Y > t.MaxY) { d1 = Math.Max(t.MinX - b.Centroid.X, b.Centroid.Y - t.MaxY); } else // if (t.MinY < b.Centroid.Y)&&(b.Centroid.Y < t.MaxY) { d1 = t.MinX - b.Centroid.X; } } else if (b.Centroid.X > t.MaxX) { if (b.Centroid.Y < t.MinY) { d1 = Math.Max(b.Centroid.X - t.MaxX, t.MinY - b.Centroid.Y); } else if (b.Centroid.Y > t.MaxY) { d1 = Math.Max(b.Centroid.X - t.MaxX, b.Centroid.Y - t.MaxY); } else { d1 = b.Centroid.X - t.MaxX; } } else // if (t.MinX =< b.Centroid.X) && (b.Centroid.X =< t.MaxX) { if (b.Centroid.Y < t.MinY) { d1 = t.MinY - b.Centroid.Y; } else if (b.Centroid.Y > t.MaxY) { d1 = b.Centroid.Y - t.MaxY; } else { return(0.0); } } double d2; if (t.Centroid.X < b.MinX) { if (t.Centroid.Y < b.MinY) { d2 = Math.Max(b.MinX - t.Centroid.X, b.MinY - t.Centroid.Y); } else if (t.Centroid.Y > b.MaxY) { d2 = Math.Max(b.MinX - t.Centroid.X, t.Centroid.Y - b.MaxY); } else // if (b.MinY < t.Centroid.Y)&&(t.Centroid.Y < b.MaxY) { d2 = b.MinX - t.Centroid.X; } } else if (t.Centroid.X > b.MaxX) { if (t.Centroid.Y < b.MinY) { d2 = Math.Max(t.Centroid.X - b.MaxX, b.MinY - t.Centroid.Y); } else if (t.Centroid.Y > b.MaxY) { d2 = Math.Max(t.Centroid.X - b.MaxX, t.Centroid.Y - b.MaxY); } else { d2 = t.Centroid.X - b.MaxX; } } else // if (b.MinX =< t.Centroid.X) && (t.Centroid.X =< b.MaxX) { if (t.Centroid.Y < b.MinY) { d2 = b.MinY - t.Centroid.Y; } else if (t.Centroid.Y > b.MaxY) { d2 = t.Centroid.Y - b.MaxY; } else { return(0.0); } } return(Math.Min(d1, d2)); }
/// <summary> /// Updates list of tracks based on current blobs. /// </summary> /// <param name="tracks">List of tracks.</param> /// <param name="thDistance">Max distance to determine when a track and a blob match.</param> /// <param name="thInactive">Max number of frames a track can be inactive.</param> /// <param name="thActive">If a track becomes inactive but it has been active less than thActive frames, the track will be deleted.</param> /// <remarks> /// Tracking based on: /// A. Senior, A. Hampapur, Y-L Tian, L. Brown, S. Pankanti, R. Bolle. Appearance Models for /// Occlusion Handling. Second International workshop on Performance Evaluation of Tracking and /// Surveillance Systems & CVPR'01. December, 2001. /// (http://www.research.ibm.com/peoplevision/PETS2001.pdf) /// </remarks> public void UpdateTracks(CvTracks tracks, double thDistance, int thInactive, int thActive) { if (tracks == null) { throw new ArgumentNullException("tracks"); } int nBlobs = this.Count; int nTracks = tracks.Count; if (nBlobs == 0) { return; } // Proximity matrix: // Last row/column is for ID/label. // Last-1 "/" is for accumulation. ProximityMatrix close = new ProximityMatrix(nBlobs, nTracks); // Initialization: int i = 0; foreach (CvBlob blob in Values) { close.AB[i] = 0; close.IB[i] = blob.Label; i++; } int maxTrackID = 0; int j = 0; foreach (CvTrack track in tracks.Values) { close.AT[j] = 0; close.AT[j] = track.Id; if (track.Id > maxTrackID) { maxTrackID = track.Id; } j++; } // Proximity matrix calculation and "used blob" list inicialization: for (i = 0; i < nBlobs; i++) { for (j = 0; j < nTracks; j++) { CvBlob b = this[close.IB[i]]; CvTrack t = tracks[close.IT[j]]; close[i, j] = (DistantBlobTrack(b, t) < thDistance) ? 1 : 0; if (close[i, j] < thDistance) { close.AB[i]++; close.AT[j]++; } } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Detect inactive tracks for (j = 0; j < nTracks; j++) { int c = close[nBlobs, j]; if (c == 0) { //cout << "Inactive track: " << j << endl; // Inactive track. CvTrack track = tracks[j]; track.Inactive++; track.Label = 0; } } // Detect new tracks for (i = 0; i < nBlobs; i++) { int c = close.AB[i]; if (c == 0) { //cout << "Blob (new track): " << maxTrackID+1 << endl; //cout << *B(i) << endl; // New track. maxTrackID++; CvBlob blob = this[i + 1]; CvTrack track = new CvTrack { Id = maxTrackID, Label = blob.Label, MinX = blob.MinX, MinY = blob.MinY, MaxX = blob.MaxX, MaxY = blob.MaxY, Centroid = blob.Centroid, LifeTime = 0, Active = 0, Inactive = 0, }; tracks[maxTrackID] = track; } } // Clustering for (j = 0; j < nTracks; j++) { int c = close.AT[j]; if (c != 0) { List <CvTrack> tt = new List <CvTrack> { tracks[j] }; List <CvBlob> bb = new List <CvBlob>(); GetClusterForTrack(j, close, nBlobs, nTracks, this, tracks, bb, tt); // Select track CvTrack track = null; int area = 0; foreach (CvTrack t in tt) { int a = (t.MaxX - t.MinX) * (t.MaxY - t.MinY); if (a > area) { area = a; track = t; } } // Select blob CvBlob blob = null; area = 0; foreach (CvBlob b in Values) { if (b.Area > area) { area = b.Area; blob = b; } } if (blob == null || track == null) { throw new NotSupportedException(); } // Update track track.Label = blob.Label; track.Centroid = blob.Centroid; track.MinX = blob.MinX; track.MinY = blob.MinY; track.MaxX = blob.MaxX; track.MaxY = blob.MaxY; if (track.Inactive != 0) { track.Active = 0; } track.Inactive = 0; // Others to inactive foreach (CvTrack t in tt) { if (t != track) { //cout << "Inactive: track=" << t->id << endl; t.Inactive++; t.Label = 0; } } } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// int[] trackKeys = new int[tracks.Count]; tracks.Keys.CopyTo(trackKeys, 0); foreach (int tkey in trackKeys) { CvTrack t = tracks[tkey]; if ((t.Inactive >= thInactive) || ((t.Inactive != 0) && (thActive != 0) && (t.Active < thActive))) { tracks.Remove(tkey); } else { t.LifeTime++; if (t.Inactive == 0) { t.Active++; } } } }