/* * 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.app.calib; import boofcv.alg.distort.RemovePerspectiveDistortion; import boofcv.alg.feature.detect.edge.GGradientToEdgeFeatures; import boofcv.alg.filter.blur.BlurImageOps; import boofcv.alg.filter.derivative.DerivativeType; import boofcv.alg.filter.derivative.GImageDerivativeOps; import boofcv.alg.misc.ImageStatistics; import boofcv.alg.misc.PixelMath; import boofcv.core.image.border.BorderType; import boofcv.io.image.ConvertBufferedImage; import boofcv.io.image.UtilImageIO; import boofcv.struct.image.GrayF32; import boofcv.struct.image.ImageType; import georegression.struct.point.Point2D_F64; import java.awt.image.BufferedImage; import java.io.File; import java.util.List; /** * Selects which image to same based on how sharp it is and then saves them to the disk * * @author Peter Abeles */ public class ImageSelectorAndSaver { public static int LENGTH = 50; RemovePerspectiveDistortion<GrayF32> removePerspective = new RemovePerspectiveDistortion<>(LENGTH, LENGTH, ImageType.single(GrayF32.class)); GrayF32 templateOriginal = new GrayF32(LENGTH, LENGTH); GrayF32 template = new GrayF32(LENGTH, LENGTH); GrayF32 weights = new GrayF32(LENGTH, LENGTH); double totalWeight; GrayF32 difference = new GrayF32(LENGTH, LENGTH); GrayF32 tempImage = new GrayF32(LENGTH, LENGTH); BufferedImage bestImage; double bestScore; double currentScore; File outputDirectory; int imageNumber = 0; public ImageSelectorAndSaver(String outputDirectory) { this.outputDirectory = new File(outputDirectory); if( !this.outputDirectory.exists() ) { if( !this.outputDirectory.mkdirs() ) { throw new RuntimeException("Can't create output directory. "+this.outputDirectory.getPath()); } } // TODO see if there are images in the output directory. Ask if it should delete it } /** * Creates a template of the fiducial and this is then used to determine how blurred the image is */ public void setTemplate(GrayF32 image, List<Point2D_F64> sides) { if( sides.size() != 4 ) throw new IllegalArgumentException("Expected 4 sidesCollision"); removePerspective.apply(image,sides.get(0),sides.get(1),sides.get(2),sides.get(3)); templateOriginal.setTo(removePerspective.getOutput()); // blur the image a bit so it doesn't have to be a perfect match GrayF32 blurred = new GrayF32(LENGTH,LENGTH); BlurImageOps.gaussian(templateOriginal,blurred,-1,2,null); // place greater importance on pixels which are around edges GrayF32 derivX = new GrayF32(LENGTH, LENGTH); GrayF32 derivY = new GrayF32(LENGTH, LENGTH); GImageDerivativeOps.gradient(DerivativeType.SOBEL,blurred,derivX,derivY, BorderType.EXTENDED); GGradientToEdgeFeatures.intensityE(derivX,derivY,weights); float max = ImageStatistics.max(weights); PixelMath.divide(weights,max,weights); totalWeight = ImageStatistics.sum(weights); // compute a normalized template for later use. Divide by the mean to add some lighting invariance template.setTo(removePerspective.getOutput()); float mean = (float)ImageStatistics.mean(template); PixelMath.divide(template,mean,template); } /** * Discard the current best image */ public synchronized void clearHistory() { bestScore = Double.MAX_VALUE; bestImage = null; } /** * Computes the sharpness score for the current image, if better than the current best image it's then saved. * @param image Gray scale input image for detector * @param sides Location of 4 corners on fiducial */ public synchronized void process(GrayF32 image, List<Point2D_F64> sides) { if( sides.size() != 4 ) throw new IllegalArgumentException("Expected 4 sidesCollision"); updateScore(image,sides); if( currentScore < bestScore ) { bestScore = currentScore; if( bestImage == null ) { bestImage = new BufferedImage(image.getWidth(), image.getHeight(),BufferedImage.TYPE_INT_RGB); } ConvertBufferedImage.convertTo(image,bestImage); } } /** * Used when you just want to update the score for visualization purposes but not update the best image. */ public synchronized void updateScore(GrayF32 image, List<Point2D_F64> sides) { removePerspective.apply(image,sides.get(0),sides.get(1),sides.get(2),sides.get(3)); GrayF32 current = removePerspective.getOutput(); float mean = (float)ImageStatistics.mean(current); PixelMath.divide(current,mean,tempImage); PixelMath.diffAbs(tempImage,template,difference); PixelMath.multiply(difference,weights,difference); // compute score as a weighted average of the difference currentScore = ImageStatistics.sum(difference)/totalWeight; } /** * Saves the image to a file */ public synchronized void save() { if( bestImage != null ) { File path = new File(outputDirectory, String.format("image%04d.png",imageNumber)); UtilImageIO.saveImage(bestImage,path.getAbsolutePath()); imageNumber++; } clearHistory(); } public double getFocusScore() { return currentScore; } public GrayF32 getTemplate() { return templateOriginal; } public GrayF32 getCurrentView() { return removePerspective.getOutput(); } }