All Downloads are FREE. Search and download functionalities are using the official Maven repository.

boofcv.abst.feature.tracker.DetectDescribeAssociate Maven / Gradle / Ivy

Go to download

BoofCV is an open source Java library for real-time computer vision and robotics applications.

There is a newer version: 0.26
Show newest version
/*
 * Copyright (c) 2011-2016, 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.abst.feature.tracker;

import boofcv.abst.feature.associate.AssociateDescription2D;
import boofcv.struct.feature.AssociatedIndex;
import boofcv.struct.feature.TupleDesc;
import boofcv.struct.image.ImageGray;
import georegression.struct.point.Point2D_F64;
import org.ddogleg.struct.FastQueue;

import java.util.ArrayList;
import java.util.List;


/**
 * 

* Base class for detect-describe-associate type trackers. Tracker works by detecting features in each image, * computing a descriptor for each feature, then associating the features together. *

* * @author Peter Abeles */ public class DetectDescribeAssociate implements PointTracker { // associates features between two images together protected AssociateDescription2D associate; // Detects features and manages descriptions protected DdaFeatureManager manager; // location of interest points protected FastQueue locDst = new FastQueue<>(10, Point2D_F64.class, false); protected FastQueue locSrc = new FastQueue<>(10, Point2D_F64.class, true); // description of interest points protected FastQueue featSrc; protected FastQueue featDst; // all tracks protected List tracksAll = new ArrayList<>(); // recently associated tracks protected List tracksActive = new ArrayList<>(); // tracks not matched to any recent features protected List tracksInactive = new ArrayList<>(); // tracks dropped by the tracker protected List tracksDropped = new ArrayList<>(); // tracks recently spawned protected List tracksNew = new ArrayList<>(); // previously declared tracks which are being recycled protected List unused = new ArrayList<>(); // Data returned by associate protected FastQueue matches; // number of features created. Used to assign unique IDs protected long featureID = 0; // should it update the feature description after each association? boolean updateDescription; // indicates if a feature was associated or not protected boolean isAssociated[] = new boolean[1]; /** * Configures tracker * * @param associate Association * @param updateDescription If true then the feature description will be updated after each image. * Typically this should be false. */ public DetectDescribeAssociate(DdaFeatureManager manager, final AssociateDescription2D associate, final boolean updateDescription ) { this.manager = manager; this.associate = associate; this.updateDescription = updateDescription; featSrc = new FastQueue<>(10, manager.getDescriptionType(), false); featDst = new FastQueue<>(10, manager.getDescriptionType(), false); } protected DetectDescribeAssociate() { } public boolean isUpdateDescription() { return updateDescription; } /** * If a feature is associated should the description be updated with the latest observation? */ public void setUpdateDescription(boolean updateDescription) { this.updateDescription = updateDescription; } @Override public void reset() { dropAllTracks(); featureID = 0; featDst.reset(); locDst.reset(); matches = null; } @Override public void process( I input ) { tracksActive.clear(); tracksInactive.clear(); tracksDropped.clear(); tracksNew.clear(); featDst.reset(); locDst.reset(); manager.detectFeatures(input, locDst, featDst); // skip if there are no features if( !tracksAll.isEmpty() ) { performTracking(); // add unassociated to the list for( int i = 0; i < tracksAll.size(); i++ ) { if( !isAssociated[i] ) tracksInactive.add(tracksAll.get(i)); } // clean up featSrc.reset(); locSrc.reset(); } } protected void performTracking() { // create source list putIntoSrcList(); // associate features together associate.setSource(locSrc, featSrc); associate.setDestination(locDst, featDst); associate.associate(); // used in spawn tracks. if null then no tracking data is assumed matches = associate.getMatches(); // Update the track state using association information updateTrackState(matches); } /** * Put existing tracks into source list for association */ protected void putIntoSrcList() { // make sure isAssociated is large enough if( isAssociated.length < tracksAll.size() ) { isAssociated = new boolean[ tracksAll.size() ]; } featSrc.reset(); locSrc.reset(); for( int i = 0; i < tracksAll.size(); i++ ) { PointTrack t = tracksAll.get(i); Desc desc = t.getDescription(); featSrc.add(desc); locSrc.add(t); isAssociated[i] = false; } } /** * Update each track's location and description (if configured to do so) mark tracks as being associated. */ protected void updateTrackState( FastQueue matches ) { // update tracks for( int i = 0; i < matches.size; i++ ) { AssociatedIndex indexes = matches.data[i]; PointTrack track = tracksAll.get(indexes.src); Point2D_F64 loc = locDst.data[indexes.dst]; track.set(loc.x, loc.y); tracksActive.add(track); // update the description if(updateDescription) { ((Desc)track.getDescription()).setTo(featDst.get(indexes.dst)); } isAssociated[indexes.src] = true; } } /** * Takes the current crop of detected features and makes them the keyframe */ @Override public void spawnTracks() { // setup data structures if( isAssociated.length < featDst.size ) { isAssociated = new boolean[ featDst.size ]; } // see which features are associated in the dst list for( int i = 0; i < featDst.size; i++ ) { isAssociated[i] = false; } if( matches != null ) { for( int i = 0; i < matches.size; i++ ) { isAssociated[matches.data[i].dst] = true; } } // create new tracks from latest unassociated detected features for( int i = 0; i < featDst.size; i++ ) { if( isAssociated[i] ) continue; Point2D_F64 loc = locDst.get(i); addNewTrack(loc.x,loc.y,featDst.get(i)); } } /** * Adds a new track given its location and description */ protected PointTrack addNewTrack( double x , double y , Desc desc ) { PointTrack p = getUnused(); p.set(x, y); ((Desc)p.getDescription()).setTo(desc); if( checkValidSpawn(p) ) { p.featureId = featureID++; tracksNew.add(p); tracksActive.add(p); tracksAll.add(p); return p; } else { unused.add(p); return null; } } /** * Returns true if a new track can be spawned here. Intended to be overloaded */ protected boolean checkValidSpawn( PointTrack p ) { return true; } /** * Returns an unused track. If there are no unused tracks then it creates a ne one. */ protected PointTrack getUnused() { PointTrack p; if( unused.size() > 0 ) { p = unused.remove( unused.size()-1 ); } else { p = new PointTrack(); p.setDescription(manager.createDescription()); } return p; } @Override public void dropAllTracks() { unused.addAll(tracksAll); tracksActive.clear(); tracksInactive.clear(); tracksAll.clear(); tracksNew.clear(); } /** * Remove from active list and mark so that it is dropped in the next cycle * * @param track The track which is to be dropped */ @Override public boolean dropTrack(PointTrack track) { if( !tracksAll.remove(track) ) return false; // the track may or may not be in the active list tracksActive.remove(track); tracksInactive.remove(track); // it must be in the all list // recycle the data unused.add(track); return true; } @Override public List getActiveTracks( List list ) { if( list == null ) list = new ArrayList<>(); list.addAll(tracksActive); return list; } @Override public List getDroppedTracks( List list ) { if( list == null ) list = new ArrayList<>(); list.addAll(tracksDropped); return list; } @Override public List getNewTracks( List list ) { if( list == null ) list = new ArrayList<>(); list.addAll(tracksNew); return list; } @Override public List getAllTracks( List list ) { if( list == null ) list = new ArrayList<>(); list.addAll(tracksAll); return list; } @Override public List getInactiveTracks(List list) { if( list == null ) list = new ArrayList<>(); list.addAll(tracksInactive); return list; } }