Skip to content

New feature: TrackByDetection - real-time multiple objects tracker - #736

Closed
ehsainit wants to merge 12 commits into
DeepLabCut:masterfrom
ehsainit:master
Closed

New feature: TrackByDetection - real-time multiple objects tracker #736
ehsainit wants to merge 12 commits into
DeepLabCut:masterfrom
ehsainit:master

Conversation

@ehsainit

@ehsainit ehsainit commented Jun 10, 2020

Copy link
Copy Markdown
Contributor

Goal & workflow

The goal is to extend DLC to support real-time multiple instances-of-one-object tracking. For simplicity and efficiency, a traditional tracking by detection approach was introduced.

How does it work ?

Candidates detections will be collected from the score-maps, all of the candidates will be assigned as new births , each having a unique identity, Kalman filter instance initialized with the first detection , a constant signifying the amount of skipped frames because of no available detection and a unique color. Afterwards, the detection-track-association problem is solved using the Hungarian algorithm. Only motion-information (Mahalanobis distance) is used.
Next, the tracks will be maintained and updated using the matched detection, only if their motion metric value is reasonable (e.g. not too high), otherwise, they will be re-assigned. Detections with no existing tracks will be assigned as births.
Finally the matched tracks Kalman filter instances will be updated using their correspondent's detection. To deal with the unmatched tracks, we keep track using the a priori information that their Kalman filter instances hold. If they remain unmatched up tp certain number of frames they will be assigned as deaths.

p4

Note: This was done in the course of my studies as a Bachelor's project in the Straw Lab at the University of Freiburg. The Straw Lab is interested in real-time flies tracking , so the cost metric & other stuff were specifically chosen with the regard to the input.

Steps:

At time step t, for a processed frame:

  1. DLC inference returns a score-map which contain probability information about the processed frame
  2. Maximum and minimum filters are applied to extract the n objects with probability greater than a threshold (i.e. local maxima) & assigned as detections
  3. Create tracks if the frame is the first or if no tracks exists
  4. Data association: associate new detections with existing tracks, according to a cost metric, remove deaths, assign new births etc.
  5. Feed relative-to-object-size bounding boxes of matches, predict coordinates for unmatches

Evaluation Results

This feature was evaluated using 1, 3 and 10 flies offline due to lack of time (back then),
Videos proprieties :

  • FPS : 25
  • Type : mp4
  • Length : 20s- 180s
  • GPU: ToDo

Here are some of the results:

pre-one-fly-y-trej
Trajectories plots of tracking process on one fly. The 4 subplots diagrams demonstrate the accuracy performs of Kalman filter prediction against the DLC1 detectors .while the top two subplots illustrate the detectors performance over 40 second video, the other two subplots shows how accurate the Kalman filtering was.

cmp-one-fly-x-trej
cmp-one-fly-y-trej

This figure demonstrate a trajectories comparison between the predictions (orange line) and the DLC1 detection (blue line) over 20 seconds video of 3 flies. Here we can see the x, y trajectories of one fly,

annafunction2

Illustration of the prediction performance of the Kalman filter upon missing measurements over a 10 frames sequence.

p1
Here we can see the time complexity of the framework. the x-axis represent the time in second and the y-axis the number of tracked objects.

In Action

ezgif-5-b5fbc677896a

ezgif-5-734c87e2408f

ezgif com-video-to-gif

@MMathisLab MMathisLab added the enhancement New feature or request label Jun 11, 2020
Comment thread deeplabcut/pose_estimation_tensorflow/predict_videos.py Outdated
Comment thread deeplabcut/pose_estimation_tensorflow/lib/single_object_tracker.py Outdated

@MMathisLab MMathisLab left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ehsainit an additional check point would be good to throw an error is it's not an appropriate tracker, i.e. if someone runs this on say two individual mice, this does not work, i.e.

Analyzing /Users/mwmathis/Desktop/two_white_mice_052820-SN-2020-05-28/videos/xxx_200000.h5
3811it [00:06, 624.46it/s]
Traceback (most recent call last):
  File "/Users/mwmathis/Documents/DeepLabCut/deeplabcut/gui/analyze_videos.py", line 417, in convert2_tracklets
    track_method=self.trackertypes.GetValue(),
  File "/Users/mwmathis/Documents/DeepLabCut/deeplabcut/pose_estimation_tensorflow/predict_videos.py", line 1464, in convert_detections2tracklets
    trackers = mot_tracker.track(bb)
  File "/Users/mwmathis/Documents/DeepLabCut/deeplabcut/pose_estimation_tensorflow/lib/single_object_tracker.py", line 54, in track
    self.tracks[i].skipped_frames = 0
IndexError: list index out of range

I would suggest a warning immediately upon parsing the pickle file, i.e. throw an error that say "this is for single objects/animals only" i.e. 1 individual in the config.yaml (or such)

Comment thread deeplabcut/pose_estimation_tensorflow/lib/single_object_tracker.py Outdated
Comment thread deeplabcut/pose_estimation_tensorflow/lib/single_object_tracker.py Outdated
@ehsainit

ehsainit commented Jun 13, 2020

Copy link
Copy Markdown
Contributor Author

@MMathisLab oh yeah right ! i forgot about that thanks for the note :)
Could you also share the data you used to produce this error so that i confirm that the error was due to different objects and not something else ?

@MMathisLab

Copy link
Copy Markdown
Member

will send you an email, thanks @ehsainit !

@AlexEMG AlexEMG mentioned this pull request Jun 17, 2020
@ehsainit

Copy link
Copy Markdown
Contributor Author

Hey @MMathisLab,

It was a small bug in code that I fixed now - the tracker did actually with the videos you sent me.
I think it could actually be used to track multiple parts of multiple animals.

Could you test again and give me some feedback ? Thanks !

@ehsainit
ehsainit requested a review from MMathisLab June 19, 2020 20:24
@MMathisLab
MMathisLab self-requested a review June 23, 2020 16:26
@MMathisLab

Copy link
Copy Markdown
Member

Hi @ehsainit so indeed it works to track multiple parts (and actually as it's code now must be multiple parts), but the "issue" is that each point is treated as a single object, i.e. the animals are not assembled, so that is why your tracker is highly similar to ours, but it does not do the "assembly" of the animal. Of course I think having a single tracker is very valuable, i.e. for instances where someone wants to track a "single point" per object, i.e. 1 point per mouse. This could be beneficial for fast tracking, etc.

Screen Shot 2020-06-23 at 12 21 08 PM

Right now though, I guess the images you post above, aside for the mouse - which is with 2.2 - the single objects are not made with 2.2 (i.e. #736 (comment)), i.e it is made with pre-2.2 networks, etc (as related to this PR: #534). As currently this crashes. So, we will work on this part such that 1 point per multi-animal is accepted.

@ehsainit

Copy link
Copy Markdown
Contributor Author

Hi @MMathisLab,
great! yes the tracker does track multiple "objects", so it does not care about the type of an object (instances of each object) hence the name multiple-objects-tracker. As you know I am a computer science student so I do not know if it would be helpful to track different points on animals separately - if it is not we have also the choice of limiting the tracking process for one object-type.

Indeed the flies were tracked using DLC1 and a different method to extract loc-maxima from the score map. What crashes currently ? let me know if I can help.

@MMathisLab

Copy link
Copy Markdown
Member

Hi @ehsainit sorry for the long pause - right now the issue is that the PAFs assume a skeleton, but if I want to use this as a SO detector, this needs to be adapted in the code. I have some working code, but have yet to debug why the NN is crashing.

in general the other trackers work on assembled animals, i.e. the general notion is that you (1) predict keypoints, (2) group together appropriate bps to assemble individual animals, and (3) track these individuals; without this you loose the animal ID. Thus, in your case - which is indeed a good case to have ;) - (1) prediction pose (2) track points (i.e. skipping assembly). This is really useful for tracking as you show above, i.e. 1 keypoint per animal/fly/mouse.

here is some modified code you can try out, which allows you to create the training set efficiently, but still crashing at the NN step. i.e. use a multi-animal video, label only 1 point per multianimalbodypart but with >1 individual then mod with this, and start training:

def getpafgraph(cfg, printnames=True):
    """ Auxiliary function that turns skeleton (list of connected bodypart pairs)
        into a list of corresponding indices (with regard to the stacked multianimal/uniquebodyparts)

        Convention: multianimalbodyparts go first!
    """
    individuals, uniquebodyparts, multianimalbodyparts = extractindividualsandbodyparts(
        cfg
    )
    # Attention this order has to be consistent (for training set creation, training, inference etc.)

    bodypartnames = multianimalbodyparts + uniquebodyparts
    lookupdict = {bodypartnames[j]: j for j in range(len(bodypartnames))}
    if len(cfg["multianimalbodyparts"])==1:
        partaffinityfield_graph = []
    else:
        if cfg["skeleton"] is None:
            cfg["skeleton"] = []

        # CHECKS if each bpt is connected to at least one other bpt
        # TODO: check that there is a path leading from each (multi)bpt to each other (multi)bpt!
        connected = set()
        partaffinityfield_graph = []
        for link in cfg["skeleton"]:
            if link[0] in bodypartnames and link[1] in bodypartnames:
                bp1 = int(lookupdict[link[0]])
                bp2 = int(lookupdict[link[1]])
                connected.add(bp1)
                connected.add(bp2)
                partaffinityfield_graph.append([bp1, bp2])
            else:
                print("Attention, parts do not exist!", link)

        unconnected = set(range(len(multianimalbodyparts))).difference(connected)
        if unconnected:
            raise ValueError(
                f'Unconnected {", ".join(multianimalbodyparts[i] for i  in unconnected)}. '
                f"For multi-animal projects, all multianimalbodyparts should be connected. "
                f"Ideally there should be at least one (multinode) path from each multianimalbodyparts to each other multianimalbodyparts. "
                f"Please verify the skeleton in the config.yaml."
            )

    if printnames:
        graph2names(cfg, partaffinityfield_graph)

    return partaffinityfield_graph

@AlexEMG AlexEMG self-assigned this Jul 11, 2020
@MMathisLab

Copy link
Copy Markdown
Member

Hi @ehsainit Hope you are well! Let me know if you have any questions, etc!

@DeepLabCut DeepLabCut deleted a comment from monajalal Sep 20, 2020
@ehsainit

ehsainit commented Nov 1, 2020

Copy link
Copy Markdown
Contributor Author

Hey @MMathisLab,
Sorry for taking so long to resolve this issue.

Regarding the NN crush upon setting partaffinityfield_graph = [], i was successfully able to fix it by setting partaffinityfield_graph in :

https://github.com/ehsainit/DeepLabCut/blob/6d6831046a3babbe8262e9c41609766a4971a85b/deeplabcut/generate_training_dataset/multiple_individuals_trainingsetmanipulation.py#L128

to false (if partaffinityfield_graph is empty i.e if there is no need for PAFs ) - ( in case of single object SO tracking - one bp per indiv ).

The training is still in process right now (I am using my CPU to train) ... it is probably going to take few days :'), no NN crushes up until now !
Screenshot from 2020-11-01 11-56-20

@MMathisLab

Copy link
Copy Markdown
Member

Hi All! as you can see there have been a lot of code changes; but we do still want to support SO tracking, so it's slated for 2.2.1 ;). If you want to merge the new code and do so, great - but I also understand it's a big time commitment, so we are happy to do so (and that would be fast for us) in another PR.

@ehsainit

ehsainit commented Aug 13, 2021

Copy link
Copy Markdown
Contributor Author

Hey Back !
Due to the limited time i got right now, i would appreciate it if you could do it in new PR :)
I would also be grateful if you can assign me in the new PR though

Best wishes,

@MMathisLab MMathisLab closed this Sep 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants