content: /*
   * Copyright (C) 2010-2014 - Mathias Unberath & Eric Goppert
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
  */
  package edu.stanford.rsl.conrad.data.numeric;
  
  import java.util.ArrayList;
  
  import edu.stanford.rsl.conrad.utils.ImageUtil;
  import ij.IJ;
  import ij.ImageJ;
  import ij.ImagePlus;
  import ij.plugin.filter.GaussianBlur;
  import ij.process.ImageProcessor;
  
  public class BurtPyramid{
  	
  	private ArrayList<Grid2D[]> precomp = null;
  	private boolean PRECOMPUTED = false;
  	
  	private ImagePlus level0 = null;
  	private int nOctaves = 0;
  	private int lvlPerOctave = 1;
  	
  	//===============================================================
  	//	Methods
  	//===============================================================
  
  	
  	public static void main(String[] args){
  		Grid2D g = ImageUtil.wrapImageProcessor(IJ.openImage("D:\\Data\\Lena.tif").getProcessor());
  		BurtPyramid pyramid = new BurtPyramid(g,3);
  		Grid2D firstLevel = pyramid.getAtOctaveAndLevel(1, 2);
  		new ImageJ();
  		g.show("Original Image");
  		firstLevel.show("Pyramid Image");
  	}
  	
  	/**
  	 * Constructor for precomputation of Pyramid levels.
  	 * @param nOctaves
  	 * @param lvlPerOctave
  	 * @param img
  	 */
  	public BurtPyramid(int nOctaves, int lvlPerOctave, Grid2D img){
  		init(nOctaves, lvlPerOctave, img);
  		precompute();
  	}
  	
  	/**
  	 * Precomputes the octaves and levels of the requested Burt pyramid and stores the grids in the respective
  	 * class member. This causes overhead, as it is called in the constructor directly.
  	 */
  	private void precompute(){
  		this.precomp = new ArrayList<Grid2D[]>();
  		for(int i = 0; i <= nOctaves; i++){
  			Grid2D[] grids = new Grid2D[lvlPerOctave];
  			for(int j = 1; j <= lvlPerOctave; j++){
  				grids[j-1] = getAtOctaveAndLevel(i, j);
  			}
  			precomp.add(grids);
  		}
  		this.PRECOMPUTED = true;
  	}
  	
  	/**
  	 * Constructor that does not pre-compute the octaves and levels. 
  	 * @param img
  	 * @param lvlPerOctave Default is 1
  	 */
  	public BurtPyramid(Grid2D img, int lvlPerOctave){
  		init(0, lvlPerOctave, img);
  	}
  	public BurtPyramid(ImagePlus img, int lvlPerOctave){
  		init(0, lvlPerOctave, img);
  	}
  	/**
  	 * Constructor that does not pre-compute the octaves and levels. 
  	 * @param img
  	 */
  	public BurtPyramid(Grid2D img){
  		init(0, 1, img);
  	}
  	
  	/**
  	 * Initializes class members.
  	 * @param nOctaves
  	 * @param lvlPerOctave
  	 * @param img
  	 */
  	private void init(int nOctaves, int lvlPerOctave, Grid2D img){
  		this.nOctaves = nOctaves;
  		this.lvlPerOctave = lvlPerOctave;
  		this.level0 = ImageUtil.wrapGrid(img, "");
  	}
  	/**
  	 * Initializes class members.
  	 * @param nOctaves
  	 * @param lvlPerOctave
  	 * @param img
  	 */
  	private void init(int nOctaves, int lvlPerOctave, ImagePlus img){
  		this.nOctaves = nOctaves;
  		this.lvlPerOctave = lvlPerOctave;
  		this.level0 = img;
  	}
  	
  	/**
  	 * Calculates (or references if precomputed) the requested Grid at the specified octave.
  	 * @param oct
  	 * @return
  	 */
  	public Grid2D getAtOctave(int oct){
  		return getAtOctaveAndLevel(oct, 1);
  	}
  	
  	/**
  	 * Calculates (or references if precomputed) the requested Grid at the specified octave and level.
  	 * @param oct
  	 * @param lvl
  	 * @return
  	 */
  	public Grid2D getAtOctaveAndLevel(int oct, int lvl){
  		assert(lvl != 0 && lvl <= lvlPerOctave) : new IllegalArgumentException("Level requested not valid.");
  		
  		if(PRECOMPUTED){
  			assert(oct < nOctaves) : new IllegalArgumentException("Octave requested not valid.");
  			return precomp.get(oct)[lvl-1];
  		}
  		
  		ImageProcessor blurred = level0.getProcessor().duplicate().convertToFloat();
  		GaussianBlur gs = new GaussianBlur();
  			
  		if(oct != 0){
  			double sigmaOct = Math.pow(2,oct);
  			gs.blurGaussian(blurred, sigmaOct, sigmaOct, 0.0002);
  		}
  			
  		int widthOct = (int)(level0.getWidth()/(oct+1.0));
  		int heightOct = (int)(level0.getHeight()/(oct+1.0));
  		blurred = blurred.resize(widthOct, heightOct);
  		//TODO level einbauen
  		double sigmaLvl = (lvl-1.0)/lvlPerOctave;
  		if(sigmaLvl != 0){
  			gs.blurGaussian(blurred, sigmaLvl, sigmaLvl, 0.0002);
  		}
  		
  		return ImageUtil.wrapImageProcessor(blurred);		
  	}
  	
  	public ArrayList<Grid2D[]> getPrecomputedPyramid(){
  		return this.precomp;
  	}
  
  }
  