/*
* 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.feature.describe;
import boofcv.abst.feature.describe.ConfigBrief;
import boofcv.abst.feature.describe.DescribeRegionPoint;
import boofcv.alg.filter.derivative.GImageDerivativeOps;
import boofcv.factory.feature.describe.FactoryDescribeRegionPoint;
import boofcv.gui.SelectAlgorithmAndInputPanel;
import boofcv.gui.feature.SelectRegionDescriptionPanel;
import boofcv.gui.feature.TupleDescPanel;
import boofcv.gui.image.ShowImages;
import boofcv.io.PathLabel;
import boofcv.io.UtilIO;
import boofcv.io.image.ConvertBufferedImage;
import boofcv.struct.feature.TupleDesc;
import boofcv.struct.image.GrayF32;
import boofcv.struct.image.ImageGray;
import boofcv.struct.image.ImageType;
import boofcv.struct.image.Planar;
import georegression.struct.point.Point2D_I32;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
/**
* Allows the user to select a point and show the description of the region at that point
*
* @author Peter Abeles
*/
public class VisualizeRegionDescriptionApp <T extends ImageGray, D extends ImageGray>
extends SelectAlgorithmAndInputPanel implements SelectRegionDescriptionPanel.Listener
{
boolean processedImage = false;
Class<T> imageType;
BufferedImage image;
DescribeRegionPoint describe;
SelectRegionDescriptionPanel panel = new SelectRegionDescriptionPanel();
TupleDescPanel tuplePanel = new TupleDescPanel();
// most recently requested pixel description. Used when the algorithm is changed
Point2D_I32 targetPt;
double targetRadius;
double targetOrientation;
public VisualizeRegionDescriptionApp( Class<T> imageType , Class<D> derivType ) {
super(1);
this.imageType = imageType;
addAlgorithm(0,"SURF-S", FactoryDescribeRegionPoint.surfStable(null, imageType));
addAlgorithm(0,"SURF-S Color", FactoryDescribeRegionPoint.surfColorStable(null, ImageType.pl(3, imageType)));
addAlgorithm(0,"SIFT", FactoryDescribeRegionPoint.sift(null,null, imageType));
addAlgorithm(0,"BRIEF", FactoryDescribeRegionPoint.brief(new ConfigBrief(true), imageType));
addAlgorithm(0,"BRIEFO", FactoryDescribeRegionPoint.brief(new ConfigBrief(false), imageType));
addAlgorithm(0,"Pixel 5x5", FactoryDescribeRegionPoint.pixel(5, 5, imageType));
addAlgorithm(0,"NCC 5x5", FactoryDescribeRegionPoint.pixelNCC(5, 5, imageType));
panel.setListener(this);
tuplePanel.setPreferredSize(new Dimension(100,50));
add(tuplePanel,BorderLayout.SOUTH);
setMainGUI(panel);
}
public void process( final BufferedImage image ) {
this.image = image;
setDescriptorInput();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
panel.setBackground(image);
panel.setPreferredSize(new Dimension(image.getWidth(),image.getHeight()));
processedImage = true;
}});
doRefreshAll();
}
private void setDescriptorInput() {
if( describe != null ) {
if( describe.getImageType().getFamily() == ImageType.Family.GRAY) {
T input = ConvertBufferedImage.convertFromSingle(image, null, imageType);
describe.setImage(input);
} else {
Planar<T> input = ConvertBufferedImage.convertFromMulti(image, null, true, imageType);
describe.setImage(input);
}
}
}
@Override
public void loadConfigurationFile(String fileName) {}
@Override
public boolean getHasProcessedImage() {
return processedImage;
}
@Override
public void refreshAll(Object[] cookies) {
setActiveAlgorithm(0,null,cookies[0]);
}
@Override
public synchronized void setActiveAlgorithm(int indexFamily, String name, Object cookie) {
this.describe = (DescribeRegionPoint<T,TupleDesc>)cookie;
setDescriptorInput();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
updateTargetDescription();
repaint();
}});
}
@Override
public void changeInput(String name, int index) {
BufferedImage image = media.openImage(inputRefs.get(index).getPath());
process(image);
}
@Override
public synchronized void descriptionChanged(Point2D_I32 pt, double radius, double orientation) {
if( pt == null || radius < 1) {
targetPt = null;
} else {
this.targetPt = pt;
this.targetRadius = radius;
this.targetOrientation = orientation;
}
updateTargetDescription();
}
/**
* Extracts the target description and updates the panel. Should only be called from a swing thread
*/
private void updateTargetDescription() {
if( targetPt != null ) {
TupleDesc feature = describe.createDescription();
describe.process(targetPt.x,targetPt.y,targetOrientation,targetRadius,feature);
tuplePanel.setDescription(feature);
} else {
tuplePanel.setDescription(null);
}
tuplePanel.repaint();
}
public static void main( String args[] ) {
Class imageType = GrayF32.class;
Class derivType = GImageDerivativeOps.getDerivativeType(imageType);
VisualizeRegionDescriptionApp app = new VisualizeRegionDescriptionApp(imageType,derivType);
java.util.List<PathLabel> inputs = new ArrayList<>();
inputs.add(new PathLabel("Cave", UtilIO.pathExample("stitch/cave_01.jpg")));
inputs.add(new PathLabel("Kayak",UtilIO.pathExample("stitch/kayak_02.jpg")));
inputs.add(new PathLabel("Forest",UtilIO.pathExample("scale/rainforest_01.jpg")));
app.setPreferredSize(new Dimension(500,500));
app.setSize(500,500);
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,"Region Descriptor Visualization", true);
}
}