/*
* 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.associate;
import boofcv.abst.feature.describe.ConfigBrief;
import boofcv.abst.feature.describe.DescribeRegionPoint;
import boofcv.abst.feature.detect.interest.ConfigFastHessian;
import boofcv.abst.feature.detect.interest.ConfigGeneralDetector;
import boofcv.abst.feature.detect.interest.ConfigSiftDetector;
import boofcv.abst.feature.detect.interest.InterestPointDetector;
import boofcv.abst.feature.orientation.OrientationImage;
import boofcv.abst.feature.orientation.OrientationIntegral;
import boofcv.alg.feature.detect.interest.GeneralFeatureDetector;
import boofcv.alg.transform.ii.GIntegralImageOps;
import boofcv.core.image.GeneralizedImageOps;
import boofcv.factory.feature.describe.FactoryDescribeRegionPoint;
import boofcv.factory.feature.detect.interest.FactoryDetectPoint;
import boofcv.factory.feature.detect.interest.FactoryInterestPoint;
import boofcv.factory.feature.orientation.FactoryOrientation;
import boofcv.factory.feature.orientation.FactoryOrientationAlgs;
import boofcv.gui.SelectAlgorithmAndInputPanel;
import boofcv.gui.feature.AssociationScorePanel;
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 georegression.struct.point.Point2D_F64;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
/**
* Shows how tightly focused the score is around the best figure by showing the relative
* size and number of features which have a similar score visually in the image. For example,
* lots of other features with similar sized circles means the distribution is spread widely
* while only one or two small figures means it is very narrow.
*
* @author Peter Abeles
*/
public class VisualizeAssociationScoreApp
extends SelectAlgorithmAndInputPanel implements VisualizeScorePanel.Listener {
// These classes process the input images and compute association score
InterestPointDetector detector;
DescribeRegionPoint describe;
OrientationImage orientation;
// gray scale versions of input image
T imageLeft;
T imageRight;
Class imageType;
// visualizes association score
AssociationScorePanel scorePanel;
VisualizeScorePanel controlPanel;
// has the image been processed yet
boolean processedImage = false;
public VisualizeAssociationScoreApp(Class imageType,
Class derivType) {
super(2);
this.imageType = imageType;
imageLeft = GeneralizedImageOps.createSingleBand(imageType, 1, 1);
imageRight = GeneralizedImageOps.createSingleBand(imageType, 1, 1);
GeneralFeatureDetector alg;
addAlgorithm(0, "Fast Hessian", FactoryInterestPoint.fastHessian(new ConfigFastHessian(1, 2, 200, 1, 9, 4, 4)));
addAlgorithm(0, "SIFT", FactoryInterestPoint.sift(null,new ConfigSiftDetector(500),imageType));
alg = FactoryDetectPoint.createShiTomasi(new ConfigGeneralDetector(500,2,1), false, derivType);
addAlgorithm(0, "Shi-Tomasi", FactoryInterestPoint.wrapPoint(alg, 1, imageType, derivType));
addAlgorithm(1, "SURF", FactoryDescribeRegionPoint.surfStable(null, imageType));
addAlgorithm(1, "SIFT", FactoryDescribeRegionPoint.sift(null,null, imageType));
addAlgorithm(1, "BRIEF", FactoryDescribeRegionPoint.brief(new ConfigBrief(true), imageType));
addAlgorithm(1, "BRIEFO", FactoryDescribeRegionPoint.brief(new ConfigBrief(false), imageType));
addAlgorithm(1, "Pixel 11x11", FactoryDescribeRegionPoint.pixel(11, 11, imageType));
addAlgorithm(1, "NCC 11x11", FactoryDescribeRegionPoint.pixelNCC(11, 11, imageType));
// estimate orientation using this once since it is fast
Class integralType = GIntegralImageOps.getIntegralType(imageType);
OrientationIntegral orientationII = FactoryOrientationAlgs.sliding_ii(null, integralType);
orientation = FactoryOrientation.convertImage(orientationII, imageType);
controlPanel = new VisualizeScorePanel(this);
scorePanel = new AssociationScorePanel(3);
JPanel gui = new JPanel();
gui.setLayout(new BorderLayout());
gui.add(controlPanel, BorderLayout.WEST);
gui.add(scorePanel, BorderLayout.CENTER);
setMainGUI(gui);
}
public void process(BufferedImage buffLeft, BufferedImage buffRight) {
// copy the input images
imageLeft.reshape(buffLeft.getWidth(), buffLeft.getHeight());
imageRight.reshape(buffRight.getWidth(), buffRight.getHeight());
ConvertBufferedImage.convertFromSingle(buffLeft, imageLeft, imageType);
ConvertBufferedImage.convertFromSingle(buffRight, imageRight, imageType);
// update the GUI's background images
scorePanel.setImages(buffLeft, buffRight);
processedImage = true;
// tell it to update everything
doRefreshAll();
}
@Override
public void loadConfigurationFile(String fileName) {
}
@Override
public void refreshAll(Object[] cookies) {
detector = (InterestPointDetector) cookies[0];
describe = (DescribeRegionPoint) cookies[1];
controlPanel.setFeatureType(describe.getDescriptionType());
processImage();
}
@Override
public void setActiveAlgorithm(int indexFamily, String name, Object cookie) {
switch (indexFamily) {
case 0:
detector = (InterestPointDetector) cookie;
break;
case 1:
describe = (DescribeRegionPoint) cookie;
break;
}
controlPanel.setFeatureType(describe.getDescriptionType());
processImage();
}
/**
* Extracts image information and then passes that info onto scorePanel for display. Data is not
* recycled to avoid threading issues.
*/
private void processImage() {
final List leftPts = new ArrayList();
final List rightPts = new ArrayList();
final List leftDesc = new ArrayList();
final List rightDesc = new ArrayList();
final ProgressMonitor progressMonitor = new ProgressMonitor(this,
"Compute Feature Information",
"", 0, 4);
extractImageFeatures(progressMonitor, 0, imageLeft, leftDesc, leftPts);
extractImageFeatures(progressMonitor, 2, imageRight, rightDesc, rightPts);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressMonitor.close();
scorePanel.setScorer(controlPanel.getSelected());
scorePanel.setLocation(leftPts, rightPts, leftDesc, rightDesc);
repaint();
}
});
}
/**
* Detects the locations of the features in the image and extracts descriptions of each of
* the features.
*/
private void extractImageFeatures(final ProgressMonitor progressMonitor, final int progress,
T image,
List descs, List locs) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressMonitor.setNote("Detecting");
}
});
detector.detect(image);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressMonitor.setProgress(progress + 1);
progressMonitor.setNote("Describing");
}
});
describe.setImage(image);
orientation.setImage(image);
// See if the detector can detect the feature's scale
if (detector.hasScale()) {
for (int i = 0; i < detector.getNumberOfFeatures(); i++) {
double yaw = 0;
Point2D_F64 pt = detector.getLocation(i);
double radius = detector.getRadius(i);
if (describe.requiresOrientation()) {
orientation.setObjectRadius(radius);
yaw = orientation.compute(pt.x, pt.y);
}
TupleDesc d = describe.createDescription();
if ( describe.process(pt.x, pt.y, yaw, radius, d) ) {
descs.add(d);
locs.add(pt.copy());
}
}
} else {
// just set the radius to one in this case
orientation.setObjectRadius(1);
for (int i = 0; i < detector.getNumberOfFeatures(); i++) {
double yaw = 0;
Point2D_F64 pt = detector.getLocation(i);
if (describe.requiresOrientation()) {
yaw = orientation.compute(pt.x, pt.y);
}
TupleDesc d = describe.createDescription();
if (describe.process(pt.x, pt.y, yaw, 1, d)) {
descs.add(d);
locs.add(pt.copy());
}
}
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressMonitor.setProgress(progress + 2);
}
});
}
@Override
public void changeInput(String name, int index) {
BufferedImage left = media.openImage(inputRefs.get(index).getPath(0));
BufferedImage right = media.openImage(inputRefs.get(index).getPath(1));
process(left, right);
}
@Override
public boolean getHasProcessedImage() {
return processedImage;
}
@Override
public void changedSetting() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
scorePanel.setScorer(controlPanel.getSelected());
repaint();
}
});
}
public static void main(String args[]) {
Class imageType = GrayF32.class;
Class derivType = GrayF32.class;
VisualizeAssociationScoreApp app = new VisualizeAssociationScoreApp(imageType, derivType);
List inputs = new ArrayList();
inputs.add(new PathLabel("Cave",
UtilIO.pathExample("stitch/cave_01.jpg"), UtilIO.pathExample("stitch/cave_02.jpg")));
inputs.add(new PathLabel("Kayak",
UtilIO.pathExample("stitch/kayak_02.jpg"), UtilIO.pathExample("stitch/kayak_03.jpg")));
inputs.add(new PathLabel("Forest",
UtilIO.pathExample("scale/rainforest_01.jpg"), UtilIO.pathExample("scale/rainforest_02.jpg")));
inputs.add(new PathLabel("Building",
UtilIO.pathExample("stitch/apartment_building_01.jpg"), UtilIO.pathExample("stitch/apartment_building_02.jpg")));
inputs.add(new PathLabel("Trees Rotate",
UtilIO.pathExample("stitch/trees_rotate_01.jpg"), UtilIO.pathExample("stitch/trees_rotate_03.jpg")));
app.setPreferredSize(new Dimension(1000, 500));
app.setSize(1000, 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, "Association Relative Score", true);
}
}
Ads help maintain this website.