boofcv.alg.tracker.combined.CombinedTrackerScalePoint Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of boofcv-feature Show documentation
Show all versions of boofcv-feature Show documentation
BoofCV is an open source Java library for real-time computer vision and robotics applications.
/*
* Copyright (c) 2011-2017, Peter Abeles. All Rights Reserved.
*
* This file is part of BoofCV (http://boofcv.org).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package boofcv.alg.tracker.combined;
import boofcv.abst.feature.associate.AssociateDescription;
import boofcv.abst.feature.detdesc.DetectDescribePoint;
import boofcv.struct.feature.AssociatedIndex;
import boofcv.struct.feature.TupleDesc;
import boofcv.struct.image.ImageGray;
import boofcv.struct.pyramid.PyramidDiscrete;
import georegression.struct.point.Point2D_F64;
import org.ddogleg.struct.FastQueue;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
*
* Combines a KLT tracker with Detect-Describe-Associate type trackers. Features are nominally tracked
* using KLT, but after KLT drops a track it is deactivated and made dormant. Upon request, it will
* attempt to reactivate a dormant track by associating it with newly detected features. After a
* track has been reactivated it will be tracked normally.
*
*
* @author Peter Abeles
*/
// TODO Two versions. One for InterestPointDetector and one for corners
public class CombinedTrackerScalePoint
, D extends ImageGray, TD extends TupleDesc> {
// current image in sequence
private I input;
// The KLT tracker used to perform the nominal track update
protected PyramidKltForCombined trackerKlt;
// feature detector and describer
protected DetectDescribePoint detector;
// Used to associate features using their DDA description
protected AssociateDescription associate;
// all active tracks that have been tracked purely by KLT
protected List> tracksPureKlt = new ArrayList<>();
// tracks that had been dropped by KLT but have been reactivated
protected List> tracksReactivated = new ArrayList<>();
// tracks that are not actively being tracked
protected List> tracksDormant = new ArrayList<>();
// recently spawned tracks
protected List> tracksSpawned = new ArrayList<>();
// track points whose data is to be reused
protected Stack> tracksUnused = new Stack<>();
// local storage used by association
protected FastQueue detectedDesc;
protected FastQueue knownDesc;
// number of tracks it has created
protected long totalTracks = 0;
// Marks a known track as being associated
private boolean associated[] = new boolean[1];
/**
* Configures tracker
*
* @param trackerKlt KLT tracker used nominally
* @param detector Feature detector
* @param associate Association algorithm
*/
public CombinedTrackerScalePoint(PyramidKltForCombined trackerKlt,
DetectDescribePoint detector,
AssociateDescription associate ) {
this.trackerKlt = trackerKlt;
this.detector = detector;
detectedDesc = new FastQueue<>(10, detector.getDescriptionType(), false);
knownDesc = new FastQueue<>(10, detector.getDescriptionType(), false);
this.associate = associate;
}
/**
* Used for unit tests
*/
protected CombinedTrackerScalePoint() {
}
/**
* Sets the tracker into its initial state. Previously declared track data structures are saved
* for re-use.
*/
public void reset() {
dropAllTracks();
totalTracks = 0;
}
/**
* Updates the location and description of tracks using KLT. Saves a reference
* to the input image for future processing.
*
* @param input Input image.
* @param pyramid Image pyramid of input.
* @param derivX Derivative pyramid of input x-axis
* @param derivY Derivative pyramid of input y-axis
*/
public void updateTracks( I input ,
PyramidDiscrete pyramid ,
D[] derivX,
D[] derivY ) {
// forget recently dropped or spawned tracks
tracksSpawned.clear();
// save references
this.input = input;
trackerKlt.setInputs(pyramid, derivX, derivY);
trackUsingKlt(tracksPureKlt);
trackUsingKlt(tracksReactivated);
}
/**
* Tracks features in the list using KLT and update their state
*/
private void trackUsingKlt(List> tracks) {
for( int i = 0; i < tracks.size(); ) {
CombinedTrack track = tracks.get(i);
if( !trackerKlt.performTracking(track.track) ) {
// handle the dropped track
tracks.remove(i);
tracksDormant.add(track);
} else {
track.set(track.track.x,track.track.y);
i++;
}
}
}
/**
* From the found interest points create new tracks. Tracks are only created at points
* where there are no existing tracks.
*
* Note: Must be called after {@link #associateAllToDetected}.
*/
public void spawnTracksFromDetected() {
// mark detected features with no matches as available
FastQueue matches = associate.getMatches();
int N = detector.getNumberOfFeatures();
for( int i = 0; i < N; i++ )
associated[i] = false;
for( AssociatedIndex i : matches.toList() ) {
associated[i.dst] = true;
}
// spawn new tracks for unassociated detected features
for( int i = 0; i < N; i++ ) {
if( associated[i])
continue;
Point2D_F64 p = detector.getLocation(i);
TD d = detectedDesc.get(i);
CombinedTrack track;
if( tracksUnused.size() > 0 ) {
track = tracksUnused.pop();
} else {
track = new CombinedTrack<>();
track.desc = detector.createDescription();
track.track = trackerKlt.createNewTrack();
}
// create the descriptor for tracking
trackerKlt.setDescription((float)p.x,(float)p.y,track.track);
// set track ID and location
track.featureId = totalTracks++;
track.desc.setTo(d);
track.set(p);
// update list of active tracks
tracksPureKlt.add(track);
tracksSpawned.add(track);
}
}
/**
* Associates pre-existing tracks to newly detected features
*
* @param known List of known tracks
*/
private void associateToDetected( List> known ) {
// initialize data structures
detectedDesc.reset();
knownDesc.reset();
// create a list of detected feature descriptions
int N = detector.getNumberOfFeatures();
for( int i = 0; i < N; i++ ) {
detectedDesc.add(detector.getDescription(i));
}
// create a list of previously created track descriptions
for( CombinedTrack t : known ) {
knownDesc.add(t.desc);
}
// associate features
associate.setSource(knownDesc);
associate.setDestination(detectedDesc);
associate.associate();
N = Math.max(known.size(),detector.getNumberOfFeatures());
if( associated.length < N )
associated = new boolean[N];
}
/**
* Associate all tracks in any state to the latest observations. If a dormant track is associated it
* will be reactivated. If a reactivated track is associated it's state will be updated. PureKLT
* tracks are left unmodified.
*/
public void associateAllToDetected() {
// initialize data structures
List> all = new ArrayList<>();
all.addAll(tracksReactivated);
all.addAll(tracksDormant);
all.addAll(tracksPureKlt);
int numTainted = tracksReactivated.size() + tracksDormant.size();
tracksReactivated.clear();
tracksDormant.clear();
// detect features
detector.detect(input);
// associate features
associateToDetected(all);
FastQueue matches = associate.getMatches();
// See which features got respawned and which ones are made dormant
for( int i = 0; i < numTainted; i++ ) {
associated[i] = false;
}
for( AssociatedIndex a : matches.toList() ) {
// don't mess with pure-KLT tracks
if( a.src >= numTainted )
continue;
CombinedTrack t = all.get(a.src);
t.set(detector.getLocation(a.dst));
trackerKlt.setDescription((float) t.x, (float) t.y, t.track);
tracksReactivated.add(t);
associated[a.src] = true;
}
for( int i = 0; i < numTainted; i++ ) {
if( !associated[i] ) {
tracksDormant.add(all.get(i));
}
}
}
/**
* Stops tracking the specified track and recycles its data.
*
* @param track The track being dropped
* @return true if the track was being tracked and data was recycled false if not.
*/
public boolean dropTrack( CombinedTrack track ) {
if( !tracksPureKlt.remove(track) )
if( !tracksReactivated.remove(track) )
if( !tracksDormant.remove(track) )
return false;
tracksUnused.add(track);
return true;
}
public List> getSpawned() {
return tracksSpawned;
}
public List> getPureKlt() {
return tracksPureKlt;
}
public List> getReactivated() {
return tracksReactivated;
}
public List> getDormant() {
return tracksDormant;
}
public PyramidKltForCombined getTrackerKlt() {
return trackerKlt;
}
public DetectDescribePoint getDetector() {
return detector;
}
/**
* Drops all tracks and recycles the data
*/
public void dropAllTracks() {
tracksUnused.addAll(tracksDormant);
tracksUnused.addAll(tracksPureKlt);
tracksUnused.addAll(tracksReactivated);
tracksSpawned.clear();
tracksPureKlt.clear();
tracksReactivated.clear();
tracksSpawned.clear();
tracksDormant.clear();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy