using BBIWARG.Utility;
using System;
using System.Collections.Generic;
namespace BBIWARG.Recognition.Tracking
{
///
/// Tracks multiple trackableObjects
///
/// The type of the TrackableObjects
/// The type of the TrackedObject
public abstract class Tracker
where T : TrackableObject
where TrackedT : TrackedObject
{
///
/// the trackedObjects
///
public List TrackedObjects;
///
/// the unique ID generator
///
protected TrackIDPool idPool;
///
/// the size of the input image (used to calculate position similarities)
///
private ImageSize imageSize;
///
/// list of similarities between trackableObjects and trackedObjects
///
private List> similarities;
///
/// Initializes a new instance of the Tracker class.
///
/// Size of the input image.
public Tracker(ImageSize imageSize)
{
this.imageSize = imageSize;
reset();
}
///
/// Calculates the similarity [0-1] between a TrackedObject and a TrackableObject.
///
/// the tracked object
/// the detected trackable object
/// the similarity [0-1]
public abstract float calculateSimilarity(TrackedT trackedObject, T detectedObject);
///
/// Resets the idPool and the trackedObjects.
///
public void reset()
{
idPool = new TrackIDPool();
TrackedObjects = new List();
}
///
/// Creates a new TrackedObject with the given TrackableObject as initial object.
///
/// the initial trackableObject
/// the TrackedObject
protected abstract TrackedT createTrackedObject(T detectedObject);
///
/// Gets a list of TrackableObjects from all TrackedObjects with the given state.
///
/// the desired tracking state
/// the list of trackableObjects with the given state
protected List getCurrentObjectsWithState(TrackingState state)
{
List objects = new List();
foreach (TrackedT trackedObject in TrackedObjects)
{
if (trackedObject.CurrentState == state)
objects.Add(trackedObject.CurrentObject);
}
return objects;
}
///
/// Calculates a similarity [0-1] between two positions with the given maximum relative distance.
///
/// the first position
/// the second position
/// the maximum distance [0-1] relative to the image size (maxAbsoluteDistance = maxRelativeDistance*imageSize.DiagonalLength)
/// the calculated similarity between the two positions
protected float getPositionSimilarity(Vector2D p1, Vector2D p2, float maxRelativeDistance)
{
float distance = p1.getDistanceTo(p2);
float maxDistance = maxRelativeDistance * imageSize.DiagonalLength;
float similarity = Math.Max(1 - distance / maxDistance, 0);
return similarity;
}
///
/// adds new TrackedObjects or updates and removes TrackedObjects with the new trackableObjects
///
/// the trackableObjects in the current frame
protected void trackObjects(List detectedObjects)
{
if (TrackedObjects.Count == 0)
{
addNewTrackedObjects(detectedObjects);
}
else
{
updateTrackedObjects(detectedObjects);
removeDeletableTrackedObjects();
}
}
///
/// Updates the trackedObjects with the detectedObjects in the current frame. Each TrackedObject is assigned the best fitting detectedObject. Each unassigned detectedObject gets a new instance of a TrackedObject, each unassigned TrackedObject gets updated with null.
///
/// the trackableObjects in the current frame
protected void updateTrackedObjects(List detectedObjects)
{
List unassignedTrackedObjects = new List(TrackedObjects);
List unassignedDetectedObjects = new List(detectedObjects);
createSimilarities(detectedObjects);
while (similarities.Count > 0)
{
Similarity maxSimilarity = similarities[0];
maxSimilarity.TrackedObject.updateFrame(maxSimilarity.DetectedObject);
unassignedDetectedObjects.Remove(maxSimilarity.DetectedObject);
unassignedTrackedObjects.Remove(maxSimilarity.TrackedObject);
removeConcurringSimilarities(maxSimilarity);
}
addNewTrackedObjects(unassignedDetectedObjects);
foreach (TrackedT trackedObject in unassignedTrackedObjects)
{
trackedObject.updateFrame(null);
}
}
///
/// creates a new TrackedObject for each trackableObject
///
/// the unassigned trackableObjects
private void addNewTrackedObjects(List detectedObjects)
{
foreach (T detectedObject in detectedObjects)
{
TrackedT trackedObject = createTrackedObject(detectedObject);
TrackedObjects.Add(trackedObject);
}
}
///
/// Creates the list of similarities by creating a similarity for each TrackedObject with each detected TrackableObject.
///
/// the trackableObjects in the current frame
private void createSimilarities(List detectedObjects)
{
similarities = new List>();
foreach (TrackedT trackedObject in TrackedObjects)
{
foreach (T detectedObject in detectedObjects)
{
float similarityValue = calculateSimilarity(trackedObject, detectedObject);
if (similarityValue > 0)
similarities.Add(new Similarity(trackedObject, detectedObject, similarityValue));
}
}
// sort depending on similarity-value
similarities.Sort((s1, s2) => s2.Value.CompareTo(s1.Value));
}
///
/// Removes all similarities with the trackedObject or the same trackableObject as the given similarity.
///
/// the similarity of two assigned objects
private void removeConcurringSimilarities(Similarity similarity)
{
for (int i = similarities.Count - 1; i >= 0; i--)
{
Similarity s = similarities[i];
if (s.TrackedObject == similarity.TrackedObject || s.DetectedObject == similarity.DetectedObject)
similarities.RemoveAt(i);
}
}
///
/// Removes all TrackedObjects, which current state is "delete".
///
private void removeDeletableTrackedObjects()
{
for (int i = TrackedObjects.Count - 1; i >= 0; i--)
{
TrackedT trackedObject = TrackedObjects[i];
if (trackedObject.CurrentState == TrackingState.Deleted)
{
idPool.setIDUnused(trackedObject.ID);
TrackedObjects.RemoveAt(i);
}
}
}
}
}