/*
* 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.fiducial;
import boofcv.abst.fiducial.FiducialDetector;
import boofcv.abst.fiducial.FiducialStability;
import boofcv.abst.fiducial.SquareImage_to_FiducialDetector;
import boofcv.abst.fiducial.calib.ConfigChessboard;
import boofcv.abst.fiducial.calib.ConfigSquareGrid;
import boofcv.abst.fiducial.calib.ConfigSquareGridBinary;
import boofcv.core.image.GConvertImage;
import boofcv.core.image.GeneralizedImageOps;
import boofcv.factory.fiducial.ConfigFiducialBinary;
import boofcv.factory.fiducial.ConfigFiducialImage;
import boofcv.factory.fiducial.FactoryFiducial;
import boofcv.factory.filter.binary.ConfigThreshold;
import boofcv.factory.filter.binary.ThresholdType;
import boofcv.gui.VideoProcessAppBase;
import boofcv.gui.fiducial.VisualizeFiducial;
import boofcv.gui.image.ImagePanel;
import boofcv.gui.image.ShowImages;
import boofcv.io.PathLabel;
import boofcv.io.UtilIO;
import boofcv.io.image.ConvertBufferedImage;
import boofcv.io.image.SimpleImageSequence;
import boofcv.struct.calib.IntrinsicParameters;
import boofcv.struct.image.GrayU8;
import boofcv.struct.image.ImageGray;
import boofcv.struct.image.ImageType;
import boofcv.struct.image.Planar;
import georegression.struct.se.Se3_F64;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Let's the user select a region in the image and tracks it using different algorithms.
*
* @author Peter Abeles
*/
public class FiducialTrackerApp
extends VideoProcessAppBase>
{
public static final String SQUARE_NUMBER = "Square Number";
public static final String SQUARE_PICTURE = "Square Picture";
public static final String CALIB_CHESS = "Chessboard";
public static final String CALIB_SQUARE_GRID = "Square Grid";
public static final String CALIB_SQUARE_BINARY_GRID = "Square Binary Grid";
private static final Font font = new Font("Serif", Font.BOLD, 14);
Class imageClass;
ImagePanel panel = new ImagePanel();
I gray;
FiducialDetector detector;
IntrinsicParameters intrinsic;
boolean processedInputImage = false;
boolean firstFrame = true;
JCheckBox computeStability = new JCheckBox("Stability");
FiducialStability stability = new FiducialStability();
List fiducialInfo = new ArrayList();
FiducialStability stabilityMax = new FiducialStability();
public FiducialTrackerApp(Class imageType) {
super(0, ImageType.pl(3, imageType));
this.imageClass = imageType;
gray = GeneralizedImageOps.createSingleBand(imageType,1,1);
panel.setPreferredSize(new Dimension(640, 480));
panel.setFocusable(true);
panel.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
isPaused = !isPaused;
}
@Override public void mousePressed(MouseEvent e) {}
@Override public void mouseReleased(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent e) {}
@Override public void mouseExited(MouseEvent e) {}
});
addToToolbar(computeStability);
computeStability.addActionListener(this);
computeStability.setSelected(true);
setMainGUI(panel);
periodSpinner.setValue(33);
}
@Override
public void process( final SimpleImageSequence> sequence ) {
// stop the image processing code
stopWorker();
this.sequence = sequence;
sequence.setLoop(true);
setPause(false);
if( !sequence.hasNext() )
throw new IllegalArgumentException("Empty sequence");
// start everything up and resume processing
doRefreshAll();
}
@Override
public void refreshAll(Object[] cookies) {
firstFrame = true;
setPause(false);
startWorkerThread();
}
@Override
public void setActiveAlgorithm(int indexFamily, String name, Object cookie) {
stopWorker();
sequence.reset();
refreshAll(null);
}
@Override
protected void updateAlg(Planar frame, BufferedImage buffImage) {
if( detector.getInputType().getFamily() == ImageType.Family.GRAY) {
gray.reshape(frame.width, frame.height);
GConvertImage.average(frame, gray);
}
detector.detect(gray);
processedInputImage = true;
}
@Override
protected void updateAlgGUI(Planar frame, BufferedImage imageGUI, double fps) {
if( firstFrame ) {
panel.setPreferredSize(new Dimension(imageGUI.getWidth(),imageGUI.getHeight()));
firstFrame = false;
}
int height = getHeight();
Graphics2D g2 = imageGUI.createGraphics();
Se3_F64 targetToSensor = new Se3_F64();
for (int i = 0; i < detector.totalFound(); i++) {
detector.getFiducialToCamera(i, targetToSensor);
double width = detector.getWidth(i);
long id = detector.getId(i);
VisualizeFiducial.drawLabelCenter(targetToSensor, intrinsic, ""+id, g2);
VisualizeFiducial.drawCube(targetToSensor, intrinsic, width, 3, g2);
if( computeStability.isSelected() ) {
handleStability(height, g2, i, id);
}
}
panel.setBufferedImageSafe(imageGUI);
panel.repaint();
}
/**
* Computes and visualizes the stability
*/
private void handleStability(int height, Graphics2D g2, int index, long fiducialID) {
FiducialInfo info = findFiducial(fiducialID);
info.totalObserved++;
g2.setFont(font);
if (detector.computeStability(index, 0.25, stability)) {
stabilityMax.location = Math.max(stability.location, stabilityMax.location);
stabilityMax.orientation = Math.max(stability.orientation, stabilityMax.orientation);
}
if (info.totalObserved > 20) {
double fractionLocation = stability.location / stabilityMax.location;
double fractionOrientation = stability.orientation / stabilityMax.orientation;
int maxHeight = (int) (height * 0.15);
int x = info.grid * 60;
int y = 10 + maxHeight;
g2.setColor(Color.BLUE);
int h = (int) (fractionLocation * maxHeight);
g2.fillRect(x, y - h, 20, h);
g2.setColor(Color.CYAN);
x += 25;
h = (int) (fractionOrientation * maxHeight);
g2.fillRect(x, y - h, 20, h);
g2.setColor(Color.RED);
g2.drawString("" + info.id, x - 20, y + 20);
}
}
private FiducialInfo findFiducial( long id ) {
for (int i = 0; i < fiducialInfo.size(); i++) {
FiducialInfo info = fiducialInfo.get(i);
if( info.id == id ) {
return info;
}
}
FiducialInfo found = new FiducialInfo();
found.id = id;
found.grid = fiducialInfo.size();
fiducialInfo.add(found);
return found;
}
@Override
public void changeInput(String name, int index) {
stopWorker();
processedInputImage = false;
String videoName = inputRefs.get(index).getPath();
String path = videoName.substring(0, videoName.lastIndexOf('/'));
ConfigThreshold configThreshold = ConfigThreshold.local(ThresholdType.LOCAL_SQUARE, 10);
if( name.compareTo(SQUARE_NUMBER) == 0 ) {
detector = FactoryFiducial.squareBinary(new ConfigFiducialBinary(0.1), configThreshold, imageClass);
} else if( name.compareTo(SQUARE_PICTURE) == 0 ) {
double length = 0.1;
detector = FactoryFiducial.squareImage(new ConfigFiducialImage(), configThreshold, imageClass);
SquareImage_to_FiducialDetector d = (SquareImage_to_FiducialDetector)detector;
String pathImg = new File(path,"../patterns").getPath();
List names = new ArrayList();
names.add("chicken.png");
names.add("yinyang.png");
for( String foo : names ) {
BufferedImage img = media.openImage(new File(pathImg,foo).getPath());
if( img == null )
throw new RuntimeException("Can't find file "+new File(pathImg,foo).getPath());
d.addPatternImage(ConvertBufferedImage.convertFromSingle(img, null, imageClass), 125, length);
}
} else if( name.compareTo(CALIB_CHESS) == 0 ) {
detector = FactoryFiducial.calibChessboard(new ConfigChessboard(7, 5, 0.03), imageClass);
} else if( name.compareTo(CALIB_SQUARE_GRID) == 0 ) {
detector = FactoryFiducial.calibSquareGrid(new ConfigSquareGrid(4, 3, 0.03, 0.03), imageClass);
} else if( name.compareTo(CALIB_SQUARE_BINARY_GRID) == 0 ) {
File configFile = new File(path,"description_4x3_3x3_4cm_2cm.txt");
try {
ConfigSquareGridBinary config =
ConfigSquareGridBinary.parseSimple(new BufferedReader(new FileReader(configFile)));
detector = FactoryFiducial.calibSquareGridBinary(config, imageClass);
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
throw new RuntimeException("Unknown selection");
}
intrinsic = UtilIO.loadXML(media.openFile(path+"/intrinsic.xml"));
detector.setIntrinsic(intrinsic);
fiducialInfo.clear();
// give it some initial values so that it doesn't look like there is huge errors right off the bat
stabilityMax.location = 0.05;
stabilityMax.orientation = 0.02;
SimpleImageSequence> video = media.openVideo(videoName, ImageType.pl(3, imageClass));
if( video == null ) {
System.err.println("Can't find video "+videoName);
System.exit(1);
}
process(video);
}
@Override
protected void handleRunningStatus(int status) {
}
@Override
public void loadConfigurationFile(String fileName) {}
@Override
public boolean getHasProcessedImage() {
return processedInputImage;
}
private class FiducialInfo {
int totalObserved;
long id;
int grid;
}
public static void main(String[] args) {
// Class type = GrayF32.class;
Class type = GrayU8.class;
FiducialTrackerApp app = new FiducialTrackerApp(type);
// app.setBaseDirectory(UtilIO.pathExample("fiducial/"));
// app.loadInputData(UtilIO.pathExample("fiducial/fiducial.txt"));
java.util.List inputs = new ArrayList();
inputs.add(new PathLabel(SQUARE_NUMBER, UtilIO.pathExample("fiducial/binary/movie.mjpeg")));
inputs.add(new PathLabel(SQUARE_PICTURE, UtilIO.pathExample("fiducial/image/video/movie.mjpeg")));
inputs.add(new PathLabel(CALIB_CHESS, UtilIO.pathExample("fiducial/chessboard/movie.mjpeg")));
inputs.add(new PathLabel(CALIB_SQUARE_GRID, UtilIO.pathExample("fiducial/square_grid/movie.mp4")));
// inputs.add(new PathLabel(CALIB_SQUARE_BINARY_GRID, UtilIO.pathExample("fiducial/binary_grid/movie.mp4")));
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, "Detecting Fiducials", true);
}
}
Ads help maintain this website.