/*
* 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.sfm.d2;
import boofcv.abst.feature.detect.interest.ConfigFastHessian;
import boofcv.abst.feature.detect.interest.ConfigGeneralDetector;
import boofcv.alg.sfm.d2.StitchingFromMotion2D;
import boofcv.alg.tracker.klt.PkltConfig;
import boofcv.factory.feature.tracker.FactoryPointTracker;
import boofcv.gui.image.ShowImages;
import boofcv.io.PathLabel;
import boofcv.io.UtilIO;
import boofcv.struct.image.GrayF32;
import boofcv.struct.image.ImageGray;
import georegression.struct.InvertibleTransform;
import georegression.struct.affine.Affine2D_F64;
import georegression.struct.homography.Homography2D_F64;
import georegression.struct.point.Point2D_F64;
import java.util.ArrayList;
import java.util.List;
/**
* Creates a mosaic from an image sequence using tracked point features. Each the input window
* moaes toward the mosaic image's boundary it is automatically reset. When reset the current
* image is put in the initial position and the mosaic distorted accordingly.
*
* @author Peter Abeles
* @param Input image type
* @param Image derivative type
*/
// TODO add support for color again
// TODO comment and clean up code
public class VideoMosaicSequentialPointApp
extends VideoStitchBaseApp
{
private static int maxFeatures = 250;
public VideoMosaicSequentialPointApp(Class imageType, Class derivType) {
super(2,imageType,true,new Mosaic2DPanel());
PkltConfig config = new PkltConfig();
config.templateRadius = 3;
config.pyramidScaling = new int[]{1,2,4,8};
ConfigFastHessian configFH = new ConfigFastHessian();
configFH.initialSampleSize = 2;
configFH.maxFeaturesPerScale = 200;
addAlgorithm(0, "KLT", FactoryPointTracker.klt(config, new ConfigGeneralDetector(maxFeatures, 3, 1),
imageType, derivType));
addAlgorithm(0, "ST-BRIEF", FactoryPointTracker.
dda_ST_BRIEF(150, new ConfigGeneralDetector(400, 1, 10), imageType, null));
// size of the description region has been increased to improve quality.
addAlgorithm(0, "ST-NCC", FactoryPointTracker.
dda_ST_NCC(new ConfigGeneralDetector(500, 3, 9), 10, 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(400, 3, 1),
config, 75, null, null, imageType, derivType));
addAlgorithm(0, "FH-SURF-KLT", FactoryPointTracker.combined_FH_SURF_KLT(
config, 75, configFH, null, null, imageType));
addAlgorithm(1,"Affine", new Affine2D_F64());
addAlgorithm(1,"Homography", new Homography2D_F64());
absoluteMinimumTracks = 40;
respawnTrackFraction = 0.3;
respawnCoverageFraction = 0.8;
maxJumpFraction = 0.3;
inlierThreshold = 4;
}
private IT createInitialTransform() {
float scale = 0.8f;
if( fitModel instanceof Affine2D_F64 ) {
Affine2D_F64 H = new Affine2D_F64(scale,0,0,scale, stitchWidth /4, stitchHeight /4);
return (IT)H.invert(null);
} else if( fitModel instanceof Homography2D_F64 ) {
Homography2D_F64 H = new Homography2D_F64(scale,0,stitchWidth /4,0,scale,stitchHeight /4,0,0,1 );
return (IT)H.invert(null);
} else {
throw new RuntimeException("Need to support this model type: "+fitModel.getClass().getSimpleName());
}
}
@Override
protected void init(int inputWidth, int inputHeight) {
setStitchImageSize(1000, 600);
((Mosaic2DPanel)gui).setMosaicSize(stitchWidth, stitchHeight);
alg.configure(stitchWidth, stitchHeight,createInitialTransform());
}
@Override
protected boolean checkLocation(StitchingFromMotion2D.Corners corners) {
if( closeToBorder(corners.p0) )
return true;
if( closeToBorder(corners.p1) )
return true;
if( closeToBorder(corners.p2) )
return true;
if( closeToBorder(corners.p3) )
return true;
return false;
}
private boolean closeToBorder( Point2D_F64 pt ) {
if( pt.x < borderTolerance || pt.y < borderTolerance)
return true;
return( pt.x >= stitchWidth - borderTolerance || pt.y >= stitchHeight - borderTolerance);
}
public static void main( String args[] ) {
Class type = GrayF32.class;
Class derivType = type;
// Class type = GrayU8.class;
// Class derivType = GrayS16.class;
VideoMosaicSequentialPointApp app = new VideoMosaicSequentialPointApp(type,derivType);
List inputs = new ArrayList();
inputs.add(new PathLabel("Plane 1", UtilIO.pathExample("mosaic/airplane01.mjpeg")));
inputs.add(new PathLabel("Plane 2", UtilIO.pathExample("mosaic/airplane02.mjpeg")));
inputs.add(new PathLabel("Shake", UtilIO.pathExample("shake.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, "Video Image Mosaic", true);
}
@Override
protected void handleRunningStatus(int status) {}
}
Ads help maintain this website.