/*
 * 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.filter.blur.BlurFilter;
import boofcv.abst.transform.wavelet.WaveletTransform;
import boofcv.alg.denoise.DenoiseWavelet;
import boofcv.alg.filter.derivative.GImageDerivativeOps;
import boofcv.alg.misc.GImageMiscOps;
import boofcv.alg.misc.GPixelMath;
import boofcv.core.image.GeneralizedImageOps;
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.gui.SelectAlgorithmAndInputPanel;
import boofcv.gui.image.ImagePanel;
import boofcv.gui.image.ShowImages;
import boofcv.io.PathLabel;
import boofcv.io.UtilIO;
import boofcv.io.image.ConvertBufferedImage;
import boofcv.struct.image.GrayF32;
import boofcv.struct.image.ImageGray;
import boofcv.struct.wavelet.WaveletDescription;
import boofcv.struct.wavelet.WlCoef;

import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Vector;


/**
 * Displays the results of denoising an image.
 *
 * @author Peter Abeles
 */
public class DenoiseVisualizeApp
	extends SelectAlgorithmAndInputPanel implements DenoiseInfoPanel.Listener
{

	// amount of noise added to the test images
	float noiseSigma;
	int numLevels;
	// config for blurring
	int blurRadius;

	Random rand = new Random(2234);

	// selected spacial filter
	BlurFilter filter;
	// selected wavelet filter
	DenoiseWavelet denoiser;
	WaveletDescription waveletDesc;
	List waveletList = new ArrayList();

	JPanel gui = new JPanel();
	DenoiseInfoPanel info = new DenoiseInfoPanel();
	ImagePanel imagePanel = new ImagePanel();

	Class imageType;
	T input;
	T noisy;
	T output;

	D deriv;

	Vector images = new Vector();

	boolean processedImage = false;

	public DenoiseVisualizeApp( Class imageType ) {
		super(1);

		this.imageType = imageType;

		addAlgorithm(0,"BayesShrink", FactoryDenoiseWaveletAlg.bayes(null,imageType));
		addAlgorithm(0,"SureShrink",FactoryDenoiseWaveletAlg.sure(imageType));
		addAlgorithm(0,"VisuShrink",FactoryDenoiseWaveletAlg.visu(imageType));
		FilterImageInterface filter;
		filter = FactoryBlurFilter.gaussian(imageType,-1,1);
		addAlgorithm(0,"Gaussian",filter);
		filter = FactoryBlurFilter.mean(imageType,1);
		addAlgorithm(0,"Mean",filter);
		filter = FactoryBlurFilter.median(imageType,1);
		addAlgorithm(0,"Median",filter);

		info.addWaveletName("Daub 4");
		waveletList.add(FactoryWaveletDaub.daubJ_F32(4));
		info.addWaveletName("Coiflet 6");
		waveletList.add(FactoryWaveletCoiflet.generate_F32(6));
		info.addWaveletName("Haar");
		waveletList.add(FactoryWaveletHaar.generate(false, 32));
		// todo something is clearly wrong with biorthogonal.  comment out so it doesn't appear in the applet
//		info.addWaveletName("Biorthogonal 5");
//		waveletList.add(FactoryWaveletDaub.biorthogonal_F32(5, BorderType.WRAP));
		waveletDesc = waveletList.get(0);

		input = GeneralizedImageOps.createSingleBand(imageType, 1, 1);
		noisy = GeneralizedImageOps.createSingleBand(imageType, 1, 1);
		output = GeneralizedImageOps.createSingleBand(imageType, 1, 1);

		Class derivType = GImageDerivativeOps.getDerivativeType(imageType);
		deriv = GeneralizedImageOps.createSingleBand(derivType, 1, 1);

		gui.setLayout(new BorderLayout());
		gui.add(info,BorderLayout.WEST);
		gui.add(imagePanel,BorderLayout.CENTER);
		info.setListener(this);

		// get initial values
		noiseSigma = info.getNoiseSigma();
		blurRadius = info.getBlurRadius();
		numLevels = info.getWaveletLevel();

		setMainGUI(gui);
	}

	public void process( BufferedImage image ) {
		input.reshape(image.getWidth(),image.getHeight());
		noisy.reshape(input.width,input.height);
		output.reshape(input.width,input.height);
		deriv.reshape(input.width,input.height);

		ConvertBufferedImage.convertFromSingle(image, input, imageType);

		// add noise to the image
		noisy.setTo(input);
		GImageMiscOps.addGaussian(noisy, rand, noiseSigma, 0, 255);
		GPixelMath.boundImage(noisy,0,255);
		// compute edge image for weighted error
		GImageDerivativeOps.laplace(input,deriv);
		GPixelMath.abs(deriv,deriv);

		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				images.clear();
				images.add(ConvertBufferedImage.convertTo(output,null,true));
				images.add(ConvertBufferedImage.convertTo(noisy,null,true));
				images.add(ConvertBufferedImage.convertTo(input,null,true));
				info.reset();
				doRefreshAll();
			}});
	}

	@Override
	public void loadConfigurationFile(String fileName) {}

	@Override
	public boolean getHasProcessedImage() {
		return processedImage;
	}

	@Override
	public void refreshAll(Object[] cookies) {
		// tod adjust menus?
		if( cookies[0] instanceof DenoiseWavelet ) {
			denoiser = (DenoiseWavelet)cookies[0];
			filter = null;
		} else {
			denoiser = null;
			filter = (BlurFilter)cookies[0];
		}

		performDenoising();
	}

	@Override
	public void setActiveAlgorithm(int indexFamily, String name, Object cookie) {
		switch( indexFamily ) {
			case 0:
				if( cookie instanceof DenoiseWavelet ) {
					denoiser = (DenoiseWavelet)cookie;
					filter = null;
				} else {
					filter = (BlurFilter)cookie;
					denoiser = null;
				}
				break;
		}

		performDenoising();
	}

	private synchronized void performDenoising() {
		if( denoiser != null ) {
			WaveletTransform waveletTran =
					FactoryWaveletTransform.create(imageType,waveletDesc,numLevels,0,255);
			FilterImageInterface filter = new WaveletDenoiseFilter(waveletTran,denoiser);

			filter.process(noisy,output);
		} else {
			filter.setRadius(blurRadius);
			filter.process(noisy,output);
		}

		final double algError = computeError((GrayF32)output,(GrayF32)input);
		final double algErrorEdge = computeWeightedError((GrayF32)output,(GrayF32)input,(GrayF32)deriv);
		final double noiseError = computeError((GrayF32)noisy,(GrayF32)input);
		final double noiseErrorEdge = computeWeightedError((GrayF32)noisy,(GrayF32)input,(GrayF32)deriv);

		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				info.setWaveletActive(denoiser!=null);
				ConvertBufferedImage.convertTo(output,images.get(0),true);
				ConvertBufferedImage.convertTo(noisy,images.get(1),true);
				info.setError(algError,algErrorEdge,noiseError,noiseErrorEdge);
				imagePanel.repaint();
				info.repaint();
				processedImage = true;
			}});
	}

	@Override
	public void changeInput(String name, int index) {
		BufferedImage image = media.openImage(inputRefs.get(index).getPath());

		if( image != null ) {
			process(image);
		}
	}

	@Override
	public void noiseChange(float sigma) {
		this.noiseSigma = sigma;
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				noisy.setTo(input);
				GImageMiscOps.addGaussian(noisy,rand,noiseSigma,0,255);
				GPixelMath.boundImage(noisy,0,255);
				performDenoising();
			}});
	}

	@Override
	public void imageChange(final int which) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				BufferedImage b = images.get(which);
				imagePanel.setBufferedImage(images.get(which));
				imagePanel.setPreferredSize(new Dimension(b.getWidth(),b.getHeight()));
				gui.validate();
				imagePanel.repaint();
			}});
	}

	@Override
	public void waveletChange(int which , int level ) {
		waveletDesc = waveletList.get(which);
		this.numLevels = level;
		performDenoising();
	}

	@Override
	public void noiseChange(int radius) {
		this.blurRadius = radius;
		performDenoising();
	}

	// todo push to what ops? Also what is this error called again?
	public static double computeError(GrayF32 imgA, GrayF32 imgB ) {
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		double total = 0;

		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {
				double difference =  Math.abs(imgA.get(x,y)-imgB.get(x,y));
				total += difference;
			}
		}

		return total / (w*h);
	}

	// todo push to what ops?
	public static double computeWeightedError(GrayF32 imgA, GrayF32 imgB ,
											  GrayF32 imgWeight ) {
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		double total = 0;
		double totalWeight = 0;

		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {
				float weight = imgWeight.get(x,y);
				double difference =  Math.abs(imgA.get(x,y)-imgB.get(x,y));
				total += difference*weight;
				totalWeight += weight;
			}
		}

		return total / totalWeight;
	}

	public static void main( String args[] ) {
		DenoiseVisualizeApp app = new DenoiseVisualizeApp(GrayF32.class);

		List inputs = new ArrayList();
		inputs.add(new PathLabel("lena", UtilIO.pathExample("standard/lena512.jpg")));
		inputs.add(new PathLabel("barbara",UtilIO.pathExample("standard/barbara.jpg")));
		inputs.add(new PathLabel("boat",UtilIO.pathExample("standard/boat.jpg")));
		inputs.add(new PathLabel("fingerprint",UtilIO.pathExample("standard/fingerprint.jpg")));

		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,"Image Noise Removal", true);

		System.out.println("Done");
	}


}

 

Ads help maintain this website.