/*
 * #%L
 * ImgLib2: a general-purpose, multidimensional image processing library.
 * %%
 * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld,
 * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke,
 * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner,
 * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert,
 * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin,
 * Jean-Yves Tinevez and Michael Zinsmaier.
 * %%
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * #L%
 */
import ij.ImageJ;

import io.scif.img.IO;
import io.scif.img.ImgIOException;

import net.imglib2.Cursor;
import net.imglib2.FinalRealInterval;
import net.imglib2.RealInterval;
import net.imglib2.RealRandomAccess;
import net.imglib2.RealRandomAccessible;
import net.imglib2.img.Img;
import net.imglib2.img.ImgFactory;
import net.imglib2.img.array.ArrayImgFactory;
import net.imglib2.img.display.imagej.ImageJFunctions;
import net.imglib2.interpolation.randomaccess.LanczosInterpolatorFactory;
import net.imglib2.interpolation.randomaccess.NLinearInterpolatorFactory;
import net.imglib2.interpolation.randomaccess.NearestNeighborInterpolatorFactory;
import net.imglib2.type.Type;
import net.imglib2.type.numeric.real.FloatType;
import net.imglib2.view.Views;

/**
 * Use three different interpolators to 10x magnify a small area
 */
public class Example7
{
	public Example7() throws ImgIOException
	{
		// open with SCIFIO
		Img< FloatType > img = IO.openImgs( "DrosophilaWing.tif", new FloatType() ).get( 0 );

		ImageJFunctions.show( img );

		// create an InterpolatorFactory RealRandomAccessible using nearst neighbor interpolation
		NearestNeighborInterpolatorFactory< FloatType > factory1 =
			new NearestNeighborInterpolatorFactory<>();

		// create an InterpolatorFactory RealRandomAccessible using linear interpolation
		NLinearInterpolatorFactory< FloatType > factory2 =
			new NLinearInterpolatorFactory<>();

		// create an InterpolatorFactory RealRandomAccessible using lanczos interpolation
		LanczosInterpolatorFactory< FloatType > factory3 =
			new LanczosInterpolatorFactory<>();

		// create a RandomAccessible using the factory and views method
		// it is important to extend the image first, the interpolation scheme might
		// grep pixels outside of the boundaries even when locations inside are queried
		// as they integrate pixel information in a local neighborhood - the size of
		// this neighborhood depends on which interpolator is used
		RealRandomAccessible< FloatType > interpolant1 = Views.interpolate(
			Views.extendMirrorSingle( img ), factory1 );
		RealRandomAccessible< FloatType > interpolant2 = Views.interpolate(
			Views.extendMirrorSingle( img ), factory2 );
		RealRandomAccessible< FloatType > interpolant3 = Views.interpolate(
			Views.extendMirrorSingle( img ), factory3 );

		// define the area in the interpolated image
		double[] min = new double[]{ 105.12, 40.43 };
		double[] max = new double[]{ 129.56, 74.933 };

		FinalRealInterval interval = new FinalRealInterval( min, max );

		ImageJFunctions.show( magnify( interpolant1, interval, new ArrayImgFactory<>( new FloatType() ), 10 ) ).setTitle( "Nearest Neighbor Interpolation" );
		ImageJFunctions.show( magnify( interpolant2, interval, new ArrayImgFactory<>( new FloatType() ), 10 ) ).setTitle( "Linear Interpolation" );
		ImageJFunctions.show( magnify( interpolant3, interval, new ArrayImgFactory<>( new FloatType() ), 10 ) ).setTitle( "Lanczos Interpolation" );
	}

	/**
	 * Compute a magnified version of a given real interval
	 *
	 * @param source - the input data
	 * @param interval - the real interval on the source that should be magnified
	 * @param factory - the image factory for the output image
	 * @param magnification - the ratio of magnification
	 * @return - an Img that contains the magnified image content
	 */
	public static < T extends Type< T > > Img< T > magnify( RealRandomAccessible< T > source,
		RealInterval interval, ImgFactory< T > factory, double magnification )
	{
		int numDimensions = interval.numDimensions();

		// compute the number of pixels of the output and the size of the real interval
		long[] pixelSize = new long[ numDimensions ];
		double[] intervalSize = new double[ numDimensions ];

		for ( int d = 0; d < numDimensions; ++d )
		{
			intervalSize[ d ] = interval.realMax( d ) - interval.realMin( d );
			pixelSize[ d ] = Math.round( intervalSize[ d ] * magnification ) + 1;
		}

		// create the output image
		Img< T > output = factory.create( pixelSize );

		// cursor to iterate over all pixels
		Cursor< T > cursor = output.localizingCursor();

		// create a RealRandomAccess on the source (interpolator)
		RealRandomAccess< T > realRandomAccess = source.realRandomAccess();

		// the temporary array to compute the position
		double[] tmp = new double[ numDimensions ];

		// for all pixels of the output image
		while ( cursor.hasNext() )
		{
			cursor.fwd();

			// compute the appropriate location of the interpolator
			for ( int d = 0; d < numDimensions; ++d )
				tmp[ d ] = cursor.getDoublePosition( d ) / output.realMax( d ) * intervalSize[ d ]
						+ interval.realMin( d );

			// set the position
			realRandomAccess.setPosition( tmp );

			// set the new value
			cursor.get().set( realRandomAccess.get() );
		}

		return output;
	}

	public static void main( String[] args ) throws ImgIOException
	{
		// open an ImageJ window
		new ImageJ();

		// run the example
		new Example7();
	}
}