Difference between revisions of "Example Track Point Features"

From BoofCV
m
m
 
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
<center>
 
<center>
{| style="width:370pt;"
+
{| class="wikitable" width="400pt"
| [[File:Example_tracking_points.jpg|Tracked point features in an image sequence.]]
+
| [[file:Example_tracking_points.jpg|400px]] || {{#ev:youtube|https://www.youtube.com/watch?v=8pn9Ebw90uk|400|center|||start=1384}}
 
|-
 
|-
!Tracked point features in an image sequence.  Blue dots are older tracks and green dots are newly spawned tracks.
+
! Tracked point features in an image sequence.  Blue dots are older tracks and green dots are newly spawned tracks. || Video Introduction
 
|}
 
|}
 
</center>
 
</center>
Line 12: Line 12:
  
 
Example Code:
 
Example Code:
* [https://github.com/lessthanoptimal/BoofCV/blob/v0.12/examples/src/boofcv/examples/ExamplePointFeatureTracker.java ExamplePointFeatureTracker.java]
+
* [https://github.com/lessthanoptimal/BoofCV/blob/v0.37/examples/src/main/java/boofcv/examples/tracking/ExamplePointFeatureTracker.java ExamplePointFeatureTracker.java]
  
 
Concepts:
 
Concepts:
Line 19: Line 19:
 
* Displaying the location of point features
 
* Displaying the location of point features
  
Relevant Applets:
+
Videos:
* [[Applet_Feature_Tracking| Point Tracker Applet]]
+
* [https://www.youtube.com/watch?v=8pn9Ebw90uk&t=1384s Point Tracker Updates 2020]
  
 
= Example Code =
 
= Example Code =
  
 
<syntaxhighlight lang="java">
 
<syntaxhighlight lang="java">
public class ExamplePointFeatureTracker< T extends ImageSingleBand, D extends ImageSingleBand>
+
/**
{
+
* <p>
 +
* Example of how to use the {@link boofcv.abst.tracker.PointTracker} to track different types of point features.
 +
* ImagePointTracker hides much of the complexity involved in tracking point features and masks
 +
* the very different underlying structures used by these different trackers.  The default trackers
 +
* provided in BoofCV are general purpose trackers, that might not be the best tracker or utility
 +
* the underlying image features the best in all situations.
 +
* </p>
 +
*
 +
* @author Peter Abeles
 +
*/
 +
public class ExamplePointFeatureTracker<T extends ImageGray<T>, D extends ImageGray<D>> {
 
// type of input image
 
// type of input image
 
Class<T> imageType;
 
Class<T> imageType;
Line 32: Line 42:
  
 
// tracks point features inside the image
 
// tracks point features inside the image
ImagePointTracker<T> tracker;
+
PointTracker<T> tracker;
  
 
// displays the video sequence and tracked features
 
// displays the video sequence and tracked features
 
ImagePanel gui = new ImagePanel();
 
ImagePanel gui = new ImagePanel();
  
public ExamplePointFeatureTracker(Class<T> imageType) {
+
int pause;
 +
 
 +
public ExamplePointFeatureTracker( Class<T> imageType, int pause ) {
 
this.imageType = imageType;
 
this.imageType = imageType;
 
this.derivType = GImageDerivativeOps.getDerivativeType(imageType);
 
this.derivType = GImageDerivativeOps.getDerivativeType(imageType);
 +
this.pause = pause;
 
}
 
}
  
Line 45: Line 58:
 
* Processes the sequence of images and displays the tracked features in a window
 
* Processes the sequence of images and displays the tracked features in a window
 
*/
 
*/
public void process(SimpleImageSequence<T> sequence) {
+
public void process( SimpleImageSequence<T> sequence ) {
  
 
// Figure out how large the GUI window should be
 
// Figure out how large the GUI window should be
 
T frame = sequence.next();
 
T frame = sequence.next();
gui.setPreferredSize(new Dimension(frame.getWidth(),frame.getHeight()));
+
gui.setPreferredSize(new Dimension(frame.getWidth(), frame.getHeight()));
ShowImages.showWindow(gui,"KTL Tracker");
+
ShowImages.showWindow(gui, "KTL Tracker", true);
  
 
// process each frame in the image sequence
 
// process each frame in the image sequence
while( sequence.hasNext() ) {
+
while (sequence.hasNext()) {
 
frame = sequence.next();
 
frame = sequence.next();
  
Line 60: Line 73:
  
 
// if there are too few tracks spawn more
 
// if there are too few tracks spawn more
if( tracker.getActiveTracks(null).size() < 100 )
+
if (tracker.getActiveTracks(null).size() < 130)
 
tracker.spawnTracks();
 
tracker.spawnTracks();
  
Line 67: Line 80:
  
 
// wait for a fraction of a second so it doesn't process to fast
 
// wait for a fraction of a second so it doesn't process to fast
BoofMiscOps.pause(100);
+
BoofMiscOps.pause(pause);
 
}
 
}
 
}
 
}
Line 74: Line 87:
 
* Draw tracked features in blue, or red if they were just spawned.
 
* Draw tracked features in blue, or red if they were just spawned.
 
*/
 
*/
private void updateGUI(SimpleImageSequence<T> sequence) {
+
private void updateGUI( SimpleImageSequence<T> sequence ) {
 
BufferedImage orig = sequence.getGuiImage();
 
BufferedImage orig = sequence.getGuiImage();
 
Graphics2D g2 = orig.createGraphics();
 
Graphics2D g2 = orig.createGraphics();
  
// draw active tracks as blue dots
+
// draw tracks with semi-unique colors so you can track individual points with your eyes
for( PointTrack p : tracker.getActiveTracks(null) ) {
+
for (PointTrack p : tracker.getActiveTracks(null)) {
VisualizeFeatures.drawPoint(g2, (int)p.x, (int)p.y, Color.blue);
+
int red = (int)(2.5*(p.featureId%100));
 +
int green = (int)((255.0/150.0)*(p.featureId%150));
 +
int blue = (int)(p.featureId%255);
 +
VisualizeFeatures.drawPoint(g2, (int)p.pixel.x, (int)p.pixel.y, new Color(red, green, blue));
 
}
 
}
  
 
// draw tracks which have just been spawned green
 
// draw tracks which have just been spawned green
for( PointTrack p : tracker.getNewTracks(null) ) {
+
for (PointTrack p : tracker.getNewTracks(null)) {
VisualizeFeatures.drawPoint(g2, (int)p.x, (int)p.y, Color.green);
+
VisualizeFeatures.drawPoint(g2, (int)p.pixel.x, (int)p.pixel.y, Color.green);
 
}
 
}
  
 
// tell the GUI to update
 
// tell the GUI to update
gui.setBufferedImage(orig);
+
gui.setImage(orig);
 
gui.repaint();
 
gui.repaint();
 
}
 
}
Line 97: Line 113:
 
*/
 
*/
 
public void createKLT() {
 
public void createKLT() {
PkltConfig<T, D> config = PkltConfig.createDefault(imageType, derivType);
+
ConfigPKlt configKlt = new ConfigPKlt();
config.maxFeatures = 200;
+
configKlt.templateRadius = 3;
config.featureRadius = 3;
+
configKlt.pyramidLevels = ConfigDiscreteLevels.levels(4);
config.pyramidScaling = new int[]{1,2,4,8};
+
 
 +
ConfigPointDetector configDetector = new ConfigPointDetector();
 +
configDetector.type = PointDetectorTypes.SHI_TOMASI;
 +
configDetector.general.maxFeatures = 600;
 +
configDetector.general.radius = 6;
 +
configDetector.general.threshold = 1;
 +
 
  
tracker = FactoryPointSequentialTracker.klt(config,1,1);
+
tracker = FactoryPointTracker.klt(configKlt, configDetector, imageType, derivType);
 
}
 
}
  
Line 109: Line 131:
 
*/
 
*/
 
public void createSURF() {
 
public void createSURF() {
tracker = FactoryPointSequentialTracker.dda_FH_SURF(200, 3, 200, 2 ,false, imageType);
+
ConfigFastHessian configDetector = new ConfigFastHessian();
 +
configDetector.maxFeaturesPerScale = 250;
 +
configDetector.extract.radius = 3;
 +
configDetector.initialSampleStep = 2;
 +
tracker = FactoryPointTracker.dda_FH_SURF_Fast(configDetector, null, null, imageType);
 
}
 
}
  
public static void main( String args[] ) throws FileNotFoundException {
+
public static void main( String[] args ) throws FileNotFoundException {
 +
Class imageType = GrayF32.class;
  
Class imageType = ImageFloat32.class;
+
MediaManager media = DefaultMediaManager.INSTANCE;
  
// loads an MJPEG video sequence
+
int pause;
VideoMjpegCodec codec = new VideoMjpegCodec();
+
SimpleImageSequence sequence =
List<byte[]> data = codec.read(new FileInputStream("../data/applet/zoom.mjpeg"));
+
media.openVideo(UtilIO.pathExample("zoom.mjpeg"), ImageType.single(imageType));
SimpleImageSequence sequence = new JpegByteImageSequence(imageType,data,true);
+
pause = 100;
 +
// media.openCamera(null,640,480,ImageType.single(imageType)); pause = 5;
 +
sequence.setLoop(true);
  
ExamplePointFeatureTracker app = new ExamplePointFeatureTracker(imageType);
+
ExamplePointFeatureTracker app = new ExamplePointFeatureTracker(imageType, pause);
  
 
// Comment or un-comment to change the type of tracker being used
 
// Comment or un-comment to change the type of tracker being used

Latest revision as of 19:59, 21 December 2020

Example tracking points.jpg
Tracked point features in an image sequence. Blue dots are older tracks and green dots are newly spawned tracks. Video Introduction

Tracking how point features move inside an image is used to extract the geometric structure and apparent motion of the scene. There are many different ways in which point features are tracked. BoofCV provides a basic tracker that hides much of this complexity and allows a variety of different trackers to be used with out modifying any of the code.

The example code below shows how to use the ImagePointTracker interface to process images and get a list of detected points. Which tracker is used can be changed by toggling comments in the main function.

Example Code:

Concepts:

  • Loading image sequences
  • Tracking point features abstractly
  • Displaying the location of point features

Videos:

Example Code

/**
 * <p>
 * Example of how to use the {@link boofcv.abst.tracker.PointTracker} to track different types of point features.
 * ImagePointTracker hides much of the complexity involved in tracking point features and masks
 * the very different underlying structures used by these different trackers.  The default trackers
 * provided in BoofCV are general purpose trackers, that might not be the best tracker or utility
 * the underlying image features the best in all situations.
 * </p>
 *
 * @author Peter Abeles
 */
public class ExamplePointFeatureTracker<T extends ImageGray<T>, D extends ImageGray<D>> {
	// type of input image
	Class<T> imageType;
	Class<D> derivType;

	// tracks point features inside the image
	PointTracker<T> tracker;

	// displays the video sequence and tracked features
	ImagePanel gui = new ImagePanel();

	int pause;

	public ExamplePointFeatureTracker( Class<T> imageType, int pause ) {
		this.imageType = imageType;
		this.derivType = GImageDerivativeOps.getDerivativeType(imageType);
		this.pause = pause;
	}

	/**
	 * Processes the sequence of images and displays the tracked features in a window
	 */
	public void process( SimpleImageSequence<T> sequence ) {

		// Figure out how large the GUI window should be
		T frame = sequence.next();
		gui.setPreferredSize(new Dimension(frame.getWidth(), frame.getHeight()));
		ShowImages.showWindow(gui, "KTL Tracker", true);

		// process each frame in the image sequence
		while (sequence.hasNext()) {
			frame = sequence.next();

			// tell the tracker to process the frame
			tracker.process(frame);

			// if there are too few tracks spawn more
			if (tracker.getActiveTracks(null).size() < 130)
				tracker.spawnTracks();

			// visualize tracking results
			updateGUI(sequence);

			// wait for a fraction of a second so it doesn't process to fast
			BoofMiscOps.pause(pause);
		}
	}

	/**
	 * Draw tracked features in blue, or red if they were just spawned.
	 */
	private void updateGUI( SimpleImageSequence<T> sequence ) {
		BufferedImage orig = sequence.getGuiImage();
		Graphics2D g2 = orig.createGraphics();

		// draw tracks with semi-unique colors so you can track individual points with your eyes
		for (PointTrack p : tracker.getActiveTracks(null)) {
			int red = (int)(2.5*(p.featureId%100));
			int green = (int)((255.0/150.0)*(p.featureId%150));
			int blue = (int)(p.featureId%255);
			VisualizeFeatures.drawPoint(g2, (int)p.pixel.x, (int)p.pixel.y, new Color(red, green, blue));
		}

		// draw tracks which have just been spawned green
		for (PointTrack p : tracker.getNewTracks(null)) {
			VisualizeFeatures.drawPoint(g2, (int)p.pixel.x, (int)p.pixel.y, Color.green);
		}

		// tell the GUI to update
		gui.setImage(orig);
		gui.repaint();
	}

	/**
	 * A simple way to create a Kanade-Lucas-Tomasi (KLT) tracker.
	 */
	public void createKLT() {
		ConfigPKlt configKlt = new ConfigPKlt();
		configKlt.templateRadius = 3;
		configKlt.pyramidLevels = ConfigDiscreteLevels.levels(4);

		ConfigPointDetector configDetector = new ConfigPointDetector();
		configDetector.type = PointDetectorTypes.SHI_TOMASI;
		configDetector.general.maxFeatures = 600;
		configDetector.general.radius = 6;
		configDetector.general.threshold = 1;


		tracker = FactoryPointTracker.klt(configKlt, configDetector, imageType, derivType);
	}

	/**
	 * Creates a SURF feature tracker.
	 */
	public void createSURF() {
		ConfigFastHessian configDetector = new ConfigFastHessian();
		configDetector.maxFeaturesPerScale = 250;
		configDetector.extract.radius = 3;
		configDetector.initialSampleStep = 2;
		tracker = FactoryPointTracker.dda_FH_SURF_Fast(configDetector, null, null, imageType);
	}

	public static void main( String[] args ) throws FileNotFoundException {
		Class imageType = GrayF32.class;

		MediaManager media = DefaultMediaManager.INSTANCE;

		int pause;
		SimpleImageSequence sequence =
				media.openVideo(UtilIO.pathExample("zoom.mjpeg"), ImageType.single(imageType));
		pause = 100;
//				media.openCamera(null,640,480,ImageType.single(imageType)); pause = 5;
		sequence.setLoop(true);

		ExamplePointFeatureTracker app = new ExamplePointFeatureTracker(imageType, pause);

		// Comment or un-comment to change the type of tracker being used
		app.createKLT();
//		app.createSURF();

		app.process(sequence);
	}
}