/* * 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.denoise; import boofcv.abst.denoise.WaveletDenoiseFilter; import boofcv.abst.filter.FilterImageInterface; import boofcv.abst.transform.wavelet.WaveletTransform; import boofcv.alg.filter.derivative.LaplacianEdge; import boofcv.alg.misc.ImageMiscOps; import boofcv.alg.misc.ImageStatistics; import boofcv.alg.misc.PixelMath; import boofcv.core.image.border.BorderType; import boofcv.factory.denoise.FactoryDenoiseWaveletAlg; import boofcv.factory.filter.blur.FactoryBlurFilter; import boofcv.factory.transform.wavelet.FactoryWaveletCoiflet; import boofcv.factory.transform.wavelet.FactoryWaveletDaub; import boofcv.factory.transform.wavelet.FactoryWaveletHaar; import boofcv.factory.transform.wavelet.FactoryWaveletTransform; import boofcv.io.UtilIO; import boofcv.io.image.ConvertBufferedImage; import boofcv.io.image.UtilImageIO; import boofcv.struct.image.GrayF32; import boofcv.struct.wavelet.WaveletDescription; import boofcv.struct.wavelet.WlCoef_F32; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; import java.util.Random; /** * Loads a sequence of tests images and runs a variety of filters through them. Prints * out the denoising error for each filter and selects the best one for each image. * * @author Peter Abeles */ public class DenoiseAccuracyStudyApp { // amount of noise added to the test images float noiseSigma = 20; Random rand = new Random(2234); GrayF32 image; GrayF32 imageNoisy; public void process( List<TestItem> filters , List<String> images ) { for( String imagePath : images ) { System.out.println("-------------------------------------------------"); System.out.println(imagePath); loadImage(imagePath); addNoiseToImage(); GrayF32 originalImage = image.clone(); GrayF32 imageDenoised = new GrayF32(image.width,image.height); System.out.println(" Noise MSE: "+computeMSE(imageNoisy)); TestItem best = null; double bestScore = Double.MAX_VALUE; for( TestItem i : filters ) { ImageMiscOps.fill(imageDenoised,0); i.filter.process(imageNoisy,imageDenoised); if( ImageStatistics.meanDiffSq(image,originalImage) != 0 ) throw new RuntimeException("Filter modified input image"); double error = computeMSE(imageDenoised); double errorEdge = computeEdgeMSE(imageDenoised); System.out.printf("%30s MSE = %8.3f Edge MSE = %8.3f\n",i.name,error,errorEdge); if( bestScore > error ) { bestScore = error; best = i; } } System.out.println("Best Filter: "+best.name); } } public static List<TestItem> createStandard(int minLevel , int maxLevel ) { List<TestItem> ret = new ArrayList<>(); ret.addAll( addSpacial() ); for( int numLevels = minLevel; numLevels <= maxLevel; numLevels++ ) { ret.addAll( createWaveletFilters(FactoryWaveletHaar.<WlCoef_F32>generate(false,32),numLevels,"Haar")); ret.addAll( createWaveletFilters(FactoryWaveletDaub.daubJ_F32(4),numLevels,"Daub-4")); ret.addAll( createWaveletFilters(FactoryWaveletCoiflet.generate_F32(6),numLevels,"Coiflet-6")); ret.addAll( createWaveletFilters(FactoryWaveletDaub.biorthogonal_F32(5, BorderType.WRAP),numLevels,"Biorthogonal-5")); } return ret; } protected static List<TestItem> createWaveletFilters( WaveletDescription<WlCoef_F32> waveletDesc , int numLevels , String waveletName ) { List<TestItem> ret = new ArrayList<>(); WaveletTransform<GrayF32, GrayF32,WlCoef_F32> waveletTran = FactoryWaveletTransform.create_F32(waveletDesc,numLevels,0,255); FilterImageInterface<GrayF32,GrayF32> filter; filter = new WaveletDenoiseFilter<>(waveletTran,FactoryDenoiseWaveletAlg.visu(GrayF32.class)); ret.add( new TestItem(filter,"Visu "+waveletName+" L = "+numLevels)); filter = new WaveletDenoiseFilter<>(waveletTran, FactoryDenoiseWaveletAlg.bayes(null,GrayF32.class)); ret.add( new TestItem(filter,"Bayes "+waveletName+" L = "+numLevels)); filter = new WaveletDenoiseFilter<>(waveletTran,FactoryDenoiseWaveletAlg.sure(GrayF32.class)); ret.add( new TestItem(filter,"Sure "+waveletName+" L = "+numLevels)); return ret; } protected static List<TestItem> addSpacial() { List<TestItem> ret = new ArrayList<>(); FilterImageInterface<GrayF32,GrayF32> filter; filter = FactoryBlurFilter.gaussian(GrayF32.class,-1,2); ret.add( new TestItem(filter,"Gaussian "+2)); filter = FactoryBlurFilter.gaussian(GrayF32.class,-1,3); ret.add( new TestItem(filter,"Gaussian "+3)); filter = FactoryBlurFilter.mean(GrayF32.class,2); ret.add( new TestItem(filter,"Mean "+2)); filter = FactoryBlurFilter.mean(GrayF32.class,3); ret.add( new TestItem(filter,"Mean "+3)); filter = FactoryBlurFilter.median(GrayF32.class,2); ret.add( new TestItem(filter,"Median "+2)); filter = FactoryBlurFilter.median(GrayF32.class,3); ret.add( new TestItem(filter,"Median "+3)); return ret; } private double computeMSE(GrayF32 imageInv) { return ImageStatistics.meanDiffSq(imageInv,image); } private double computeEdgeMSE(GrayF32 imageInv) { GrayF32 edge = new GrayF32(imageInv.width,imageInv.height); LaplacianEdge.process(image,edge); PixelMath.abs(edge,edge); float max = ImageStatistics.maxAbs(edge); PixelMath.divide(edge,max,edge); float total = ImageStatistics.sum(edge); double error = 0; for( int y = 0; y < image.height; y++ ) { for( int x = 0; x < image.width; x++ ) { double w = edge.get(x,y)/total; double e = (image.get(x,y)-imageInv.get(x,y)); error += (e*e)*w; } } return error; } private void loadImage( String imagePath ) { BufferedImage in = UtilImageIO.loadImage(imagePath); image = ConvertBufferedImage.convertFrom(in,(GrayF32)null); } private void addNoiseToImage() { imageNoisy = image.clone(); ImageMiscOps.addGaussian(imageNoisy,rand,noiseSigma,0,255); } public static void main( String args[] ) { DenoiseAccuracyStudyApp app = new DenoiseAccuracyStudyApp(); String path = UtilIO.pathExample("standard/"); List<String> fileNames = new ArrayList<>(); fileNames.add(path+"barbara.jpg"); fileNames.add(path+"lena512.jpg"); fileNames.add(path+"peppers256.jpg"); fileNames.add(path+"boat.jpg"); fileNames.add(path+"house.png"); app.process(createStandard(2,4),fileNames); } public static class TestItem { public FilterImageInterface<GrayF32,GrayF32> filter; public String name; public double opsPerSecond; public TestItem(FilterImageInterface<GrayF32, GrayF32> filter, String name) { this.filter = filter; this.name = name; } } }