/*
 * 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.demonstrations.tracker;

import boofcv.abst.feature.detect.interest.ConfigFastHessian;
import boofcv.abst.feature.detect.interest.ConfigGeneralDetector;
import boofcv.abst.feature.tracker.PointTrack;
import boofcv.abst.feature.tracker.PointTracker;
import boofcv.alg.tracker.klt.PkltConfig;
import boofcv.factory.feature.tracker.FactoryPointTracker;
import boofcv.gui.VideoProcessAppBase;
import boofcv.gui.feature.VisualizeFeatures;
import boofcv.gui.image.ImagePanel;
import boofcv.gui.image.ShowImages;
import boofcv.io.PathLabel;
import boofcv.io.UtilIO;
import boofcv.io.image.SimpleImageSequence;
import boofcv.struct.image.GrayF32;
import boofcv.struct.image.ImageGray;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

/**
 * Runs a KLT tracker through a video sequence
 *
 * @author Peter Abeles
 */
// todo extract out base class for handling videos
public class VideoTrackerPointFeaturesApp
		extends VideoProcessAppBase implements MouseListener
{

	int maxFeatures = 400;
	int minFeatures = 150;

	PointTracker tracker;

	ImagePanel gui = new ImagePanel();

	BufferedImage workImage;

	public VideoTrackerPointFeaturesApp(Class imageType, Class derivType) {
		super(1,imageType);

		PkltConfig config = new PkltConfig();
		config.templateRadius = 3;
		config.pyramidScaling = new int[]{1,2,4,8};

		ConfigFastHessian configFH = new ConfigFastHessian();
		configFH.maxFeaturesPerScale = 200;
		configFH.extractRadius = 4;
		configFH.detectThreshold = 15f;

		addAlgorithm(0,"KLT", FactoryPointTracker.klt(config, new ConfigGeneralDetector(maxFeatures, 1, 3),
				imageType, derivType));
		addAlgorithm(0,"ST-BRIEF", FactoryPointTracker.
				dda_ST_BRIEF(200, new ConfigGeneralDetector(maxFeatures, 3, 1), imageType, derivType));
		addAlgorithm(0,"ST-NCC", FactoryPointTracker.
				dda_ST_NCC(new ConfigGeneralDetector(maxFeatures, 3, 2), 5, imageType, derivType));
		addAlgorithm(0,"FH-SURF", FactoryPointTracker.
				dda_FH_SURF_Fast(configFH, null, null, imageType));
		addAlgorithm(0,"ST-SURF-KLT", FactoryPointTracker.
				combined_ST_SURF_KLT(new ConfigGeneralDetector(maxFeatures, 3, 1),
						config, 50, null, null, imageType, derivType));
		addAlgorithm(0,"FH-SURF-KLT", FactoryPointTracker.combined_FH_SURF_KLT(
				config, 50, configFH, null, null, imageType));

		gui.addMouseListener(this);
		gui.requestFocus();
		setMainGUI(gui);
	}

	@Override
	public void loadConfigurationFile(String fileName) {}

	@Override
	public void process( SimpleImageSequence sequence ) {
		stopWorker();
		this.sequence = sequence;
		sequence.setLoop(true);
		doRefreshAll();
	}

	@Override
	public boolean getHasProcessedImage() {
		return workImage != null;
	}

	@Override
	public void refreshAll(Object[] cookies) {
		setActiveAlgorithm(0,null,cookies[0]);
	}

	@Override
	public void setActiveAlgorithm(int indexFamily, String name, Object cookie) {
		if( sequence == null )
			return;
		
		stopWorker();

		tracker = (PointTracker)cookie;
		sequence.reset();

		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				I image = sequence.next();
				gui.setPreferredSize(new Dimension(image.width,image.height));
				workImage = new BufferedImage(image.width,image.height,BufferedImage.TYPE_INT_BGR);
				gui.setBufferedImage(workImage);
				revalidate();
				startWorkerThread();
			}});
	}

	void renderFeatures( BufferedImage orig , double trackerFPS ) {
		Graphics2D g2 = workImage.createGraphics();

		g2.drawImage(orig,0,0,orig.getWidth(),orig.getHeight(),null);
		for( PointTrack p : tracker.getActiveTracks(null) ) {
			int x = (int)p.x;
			int y = (int)p.y;

			VisualizeFeatures.drawPoint(g2,x,y,Color.blue);
		}

		for( PointTrack p : tracker.getNewTracks(null) ) {
			int x = (int)p.x;
			int y = (int)p.y;

			VisualizeFeatures.drawPoint(g2,x,y,Color.green);
		}

		g2.setColor(Color.WHITE);
		g2.fillRect(5,15,140,20);
		g2.setColor(Color.BLACK);
		g2.drawString(String.format("Tracker FPS: %3.1f",trackerFPS),10,30);
	}

	@Override
	protected void updateAlg(I frame, BufferedImage buffImage) {
		tracker.process(frame);

		if( tracker.getActiveTracks(null).size() < minFeatures ) {
			tracker.spawnTracks();
		}
	}

	@Override
	protected void handleRunningStatus(int status) {}

	@Override
	protected void updateAlgGUI(ImageGray frame, BufferedImage imageGUI, double fps) {
		renderFeatures((BufferedImage)sequence.getGuiImage(),fps);
	}

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

//		Class imageType = GrayU8.class;
//		Class derivType = GrayS16.class;

		VideoTrackerPointFeaturesApp app = new VideoTrackerPointFeaturesApp(imageType, derivType);

		java.util.List inputs = new ArrayList();
		inputs.add(new PathLabel("Shake", UtilIO.pathExample("shake.mjpeg")));
		inputs.add(new PathLabel("Zoom", UtilIO.pathExample("zoom.mjpeg")));
		inputs.add(new PathLabel("Rotate", UtilIO.pathExample("rotate.mjpeg")));

		app.setInputList(inputs);

		// wait for it to process one image so that the size isn't all screwed up
		while( !app.getHasProcessedImage() ) {
			Thread.yield();
		}

		ShowImages.showWindow(app, "Feature Tracker",true);
	}
}

 

Ads help maintain this website.