/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.image.interpolation;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import javax.imageio.ImageTypeSpecifier;
import org.apache.sis.geometry.Envelope2D;
import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.geometry.Envelopes;
import org.geotoolkit.image.iterator.PixelIterator;
import org.geotoolkit.image.iterator.PixelIteratorFactory;
import org.apache.sis.referencing.CRS;
import static org.junit.Assert.*;
import org.junit.Test;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.ProjectedCRS;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.NoninvertibleTransformException;
import org.opengis.referencing.operation.TransformException;
import org.opengis.util.FactoryException;
/**
* Test resampling class.
*
* @author RĂ©mi Marechal (Geomatys).
*/
public class ResampleTest extends org.geotoolkit.test.TestBase {
/**
* Expected result about neighbor interpolation.
*/
private static double[] NEIGHBOR_RESULT = new double[]{1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,
1,1,1,2,2,2,1,1,1,
1,1,1,2,2,2,1,1,1,
1,1,1,2,2,2,1,1,1,
1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1};
/**
* Expected result about bilinear interpolation.
*/
private static double[] BILINEAR_RESULT = new double[]{0,0, 0, 0, 0, 0, 0, 0,0,
0,1, 1, 1, 1, 1, 1, 1,0,
0,1, 1.111111111, 1.222222222, 1.333333333, 1.222222222, 1.111111111, 1,0,
0,1, 1.222222222, 1.444444444, 1.666666666, 1.444444444, 1.222222222, 1,0,
0,1, 1.333333333, 1.666666666, 2, 1.666666666, 1.333333333, 1,0,
0,1, 1.222222222, 1.444444444, 1.666666666, 1.444444444, 1.222222222, 1,0,
0,1, 1.111111111, 1.222222222, 1.333333333, 1.222222222, 1.111111111, 1,0,
0,1, 1, 1, 1, 1, 1, 1,0,
0,0, 0, 0, 0, 0, 0, 0,0};
/**
* Expected result about bilinear interpolation.
*/
private static double[] BILINEAR_RESULT_W_F = new double[]{-1000,-1000, -1000, -1000, -1000, -1000, -1000, -1000,-1000,
-1000, 1, 1, 1, 1, 1, 1, 1,-1000,
-1000, 1, 1.111111111, 1.222222222, 1.333333333, 1.222222222, 1.111111111, 1,-1000,
-1000, 1, 1.222222222, 1.444444444, 1.666666666, 1.444444444, 1.222222222, 1,-1000,
-1000, 1, 1.333333333, 1.666666666, 2, 1.666666666, 1.333333333, 1,-1000,
-1000, 1, 1.222222222, 1.444444444, 1.666666666, 1.444444444, 1.222222222, 1,-1000,
-1000, 1, 1.111111111, 1.222222222, 1.333333333, 1.222222222, 1.111111111, 1,-1000,
-1000, 1, 1, 1, 1, 1, 1, 1,-1000,
-1000,-1000, -1000, -1000, -1000, -1000, -1000, -1000,-1000};
/**
* Expected result about bicubic interpolation.
*/
private static double[] BICUBIC_Result = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 1.0, 1.0, 1.0, 1.0, 1, 1, 1, 1, 1, 1, 0.0,
0.0, 1.0, 1.1975308641975309, 1.3456790123456792, 1.4444444444444444, 1.4938271604938276, 1.4938271604938276, 1.4444444444444444, 1.3456790123456788, 1.1975308641975306, 1, 0.0,
0.0, 1.0, 1.345679012345679, 1.6049382716049385, 1.7777777777777781, 1.8641975308641983, 1.8641975308641983, 1.7777777777777781, 1.604938271604938, 1.345679012345679, 1, 0.0,
0.0, 1.0, 1.4444444444444444, 1.777777777777778, 2, 2.1111111111111125, 2.1111111111111125, 2, 1.7777777777777772, 1.4444444444444446, 1, 0.0,
0.0, 1, 1.4938271604938271, 1.8641975308641974, 2.1111111111111116, 2.2345679012345694, 2.2345679012345694, 2.1111111111111116, 1.8641975308641967, 1.4938271604938274, 1, 0.0,
0.0, 1, 1.493827160493827, 1.8641975308641971, 2.1111111111111116, 2.23456790123457, 2.23456790123457, 2.1111111111111116, 1.8641975308641963, 1.4938271604938274, 1, 0.0,
0.0, 1, 1.444444444444444, 1.7777777777777772, 2, 2.111111111111114, 2.111111111111114, 2, 1.777777777777776, 1.4444444444444446, 1, 0.0,
0.0, 1, 1.3456790123456783, 1.6049382716049374, 1.7777777777777781, 1.8641975308642, 1.8641975308642, 1.7777777777777788, 1.6049382716049356, 1.345679012345679, 1, 0.0,
0.0, 1, 1.19753086419753, 1.3456790123456774, 1.4444444444444446, 1.4938271604938305, 1.4938271604938305, 1.444444444444445, 1.3456790123456754, 1.1975308641975306, 1, 0.0,
0.0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
/**
* Transformation applicate to source or destination image to considered a pixel orientation center.
*/
private static MathTransform pixelInCellCenter = new AffineTransform2D(1, 0, 0, 1, 0.5, 0.5);
/**
* Transformation applicate on source image for resampling.
*/
private MathTransform mathTransform;
/**
* Destination image.
* Resampling result.
*/
private WritableRenderedImage targetImage;
/**
* Source image.
* Image within interpolation computing is applicate.
*/
private WritableRenderedImage sourceImg;
public ResampleTest() {
final ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
final ColorModel cm = new ComponentColorModel(cs, new int[]{Double.SIZE}, false, false, Transparency.OPAQUE, DataBuffer.TYPE_DOUBLE);
final ImageTypeSpecifier imgTypeSpec = new ImageTypeSpecifier(cm, cm.createCompatibleSampleModel(1, 1));
sourceImg = imgTypeSpec.createBufferedImage(3, 3);
final WritableRaster raster = sourceImg.getWritableTile(0, 0);
raster.setSample(0, 0, 0, 1);
raster.setSample(1, 0, 0, 1);
raster.setSample(2, 0, 0, 1);
raster.setSample(0, 1, 0, 1);
raster.setSample(1, 1, 0, 2);
raster.setSample(2, 1, 0, 1);
raster.setSample(0, 2, 0, 1);
raster.setSample(1, 2, 0, 1);
raster.setSample(2, 2, 0, 1);
}
/**
* Test result obtained from neighBor interpolation and a resampling.
*
* @throws NoninvertibleTransformException
* @throws FactoryException
* @throws TransformException
*/
@Test
// @Ignore
public void jaiNeighBorTest() throws NoninvertibleTransformException, FactoryException, TransformException {
setTargetImage(9, 9, DataBuffer.TYPE_DOUBLE, -1000);
setAffineMathTransform(MathTransforms.concatenate(pixelInCellCenter, new AffineTransform2D(3, 0, 0, 3, 0, 0), pixelInCellCenter.inverse()));
/*
* Resampling
*/
Resample resample = new Resample(mathTransform.inverse(), targetImage, sourceImg,
InterpolationCase.NEIGHBOR, ResampleBorderComportement.FILL_VALUE, new double[]{0});
resample.fillImage();
Raster coverageRaster = targetImage.getTile(0, 0);
java.awt.image.DataBufferDouble datadouble = (java.awt.image.DataBufferDouble) coverageRaster.getDataBuffer();
assertArrayEquals(NEIGHBOR_RESULT, datadouble.getData(0), 1E-9);
}
/**
* Test result obtained from biLinear interpolation and a resampling.
*
* @throws NoninvertibleTransformException
* @throws FactoryException
* @throws TransformException
*/
@Test
// @Ignore
public void jaiBiLinearTest() throws NoninvertibleTransformException, FactoryException, TransformException {
setTargetImage(9, 9, DataBuffer.TYPE_DOUBLE, -1000);
setAffineMathTransform(MathTransforms.concatenate(pixelInCellCenter, new AffineTransform2D(3, 0, 0, 3, 0, 0), pixelInCellCenter.inverse()));
/*
* Resampling
*/
final Resample resample = new Resample(mathTransform.inverse(), targetImage, sourceImg,
InterpolationCase.BILINEAR, ResampleBorderComportement.FILL_VALUE, new double[]{0});
resample.fillImage();
final Raster coverageRaster = targetImage.getTile(0, 0);
java.awt.image.DataBufferDouble datadouble = (java.awt.image.DataBufferDouble) coverageRaster.getDataBuffer();
assertArrayEquals(BILINEAR_RESULT, datadouble.getData(0), 1E-9);
}
/**
* Test result obtained from biLinear interpolation and a resampling and without any fillvalue.
*
* @throws NoninvertibleTransformException
* @throws FactoryException
* @throws TransformException
*/
@Test
// @Ignore
public void withoutFillValueBiLinearTest() throws NoninvertibleTransformException, FactoryException, TransformException {
setTargetImage(9, 9, DataBuffer.TYPE_DOUBLE, -1000);
setAffineMathTransform(MathTransforms.concatenate(pixelInCellCenter, new AffineTransform2D(3, 0, 0, 3, 0, 0), pixelInCellCenter.inverse()));
/*
* Resampling
*/
final Resample resample = new Resample(mathTransform.inverse(), targetImage, sourceImg,
InterpolationCase.BILINEAR, ResampleBorderComportement.FILL_VALUE, null);
resample.fillImage();
final Raster coverageRaster = targetImage.getTile(0, 0);
java.awt.image.DataBufferDouble datadouble = (java.awt.image.DataBufferDouble) coverageRaster.getDataBuffer();
assertArrayEquals(BILINEAR_RESULT_W_F, datadouble.getData(0), 1E-9);
}
/**
* Test result obtained from biCubic interpolation and a resampling.
*
* @throws NoninvertibleTransformException
* @throws FactoryException
* @throws TransformException
*/
@Test
// @Ignore
public void jaiBiCubicTest() throws NoninvertibleTransformException, FactoryException, TransformException {
final ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
final ColorModel cm = new ComponentColorModel(cs, new int[]{Double.SIZE}, false, false, Transparency.OPAQUE, DataBuffer.TYPE_DOUBLE);
final ImageTypeSpecifier imgTypeSpec = new ImageTypeSpecifier(cm, cm.createCompatibleSampleModel(1, 1));
sourceImg = imgTypeSpec.createBufferedImage(4, 4);
final WritableRaster raster = sourceImg.getWritableTile(0, 0);
//band0
raster.setSample(0, 0, 0, 1);
raster.setSample(1, 0, 0, 1);
raster.setSample(2, 0, 0, 1);
raster.setSample(3, 0, 0, 1);
raster.setSample(0, 1, 0, 1);
raster.setSample(1, 1, 0, 2);
raster.setSample(2, 1, 0, 2);
raster.setSample(3, 1, 0, 1);
raster.setSample(0, 2, 0, 1);
raster.setSample(1, 2, 0, 2);
raster.setSample(2, 2, 0, 2);
raster.setSample(3, 2, 0, 1);
raster.setSample(0, 3, 0, 1);
raster.setSample(1, 3, 0, 1);
raster.setSample(2, 3, 0, 1);
raster.setSample(3, 3, 0, 1);
setTargetImage(12, 12, DataBuffer.TYPE_DOUBLE, -1000);
setAffineMathTransform(MathTransforms.concatenate(pixelInCellCenter, new AffineTransform2D(3, 0, 0, 3, 0, 0), pixelInCellCenter.inverse()));
/*
* Resampling
*/
final Resample resample = new Resample(mathTransform.inverse(), targetImage, sourceImg,
InterpolationCase.BICUBIC, ResampleBorderComportement.FILL_VALUE, new double[]{0});
resample.fillImage();
final Raster coverageRaster = targetImage.getTile(0, 0);
java.awt.image.DataBufferDouble datadouble = (java.awt.image.DataBufferDouble) coverageRaster.getDataBuffer();
assertArrayEquals(BICUBIC_Result, datadouble.getData(0), 1E-9);
}
/**
* Effectuate Resample test which use internaly a grid.
* This test, test bilinear resample from computed coordinate into grid and moreover image sample values.
*/
@Test
public void gridCoordinateTest() throws NoSuchAuthorityCodeException, FactoryException, TransformException {
final ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
final ColorModel cm = new ComponentColorModel(cs, new int[]{Double.SIZE}, false, false, Transparency.OPAQUE, DataBuffer.TYPE_DOUBLE);
final ImageTypeSpecifier imgTypeSpec = new ImageTypeSpecifier(cm, cm.createCompatibleSampleModel(1, 1));
sourceImg = imgTypeSpec.createBufferedImage(4, 4);
final WritableRaster raster = sourceImg.getWritableTile(0, 0);
//band0
raster.setSample(0, 0, 0, 1);
raster.setSample(1, 0, 0, 1);
raster.setSample(2, 0, 0, 1);
raster.setSample(3, 0, 0, 1);
raster.setSample(0, 1, 0, 1);
raster.setSample(1, 1, 0, 2);
raster.setSample(2, 1, 0, 2);
raster.setSample(3, 1, 0, 1);
raster.setSample(0, 2, 0, 1);
raster.setSample(1, 2, 0, 2);
raster.setSample(2, 2, 0, 2);
raster.setSample(3, 2, 0, 1);
raster.setSample(0, 3, 0, 1);
raster.setSample(1, 3, 0, 1);
raster.setSample(2, 3, 0, 1);
raster.setSample(3, 3, 0, 1);
setTargetImage(12, 12, DataBuffer.TYPE_DOUBLE, -1000);
//-- creation du crs
final CoordinateReferenceSystem crs = CRS.forCode("EPSG:2154");//-- world mercator 3395
final ProjectedCRS projCRS = (ProjectedCRS) crs;
final CoordinateReferenceSystem baseCrs = projCRS.getBaseCRS();
final MathTransform mt = projCRS.getConversionFromBase().getMathTransform(); //-- source to dest
// final Envelope srcEnv = new Envelope2D(baseCrs, 20, 44.5, 45, 45); //-- geographic
final Envelope srcEnv = new Envelope2D(baseCrs, 45,-8, 5, 16); //-- geographic
final Envelope destEnv = Envelopes.transform(mt, srcEnv);//-- dest envelop
final AffineTransform2D srcGridToCrs = new AffineTransform2D(srcEnv.getSpan(0) / 4, 0, 0, -srcEnv.getSpan(1) / 4, srcEnv.getMinimum(0), srcEnv.getMaximum(1));
final AffineTransform2D destGridToCrs = new AffineTransform2D(destEnv.getSpan(0) / 12, 0, 0, -destEnv.getSpan(1) / 12, destEnv.getMinimum(0), destEnv.getMaximum(1));
final MathTransform pixSrcGridToCrs = MathTransforms.concatenate(pixelInCellCenter, srcGridToCrs);
final MathTransform pixDestGridToProjCrs = MathTransforms.concatenate(pixelInCellCenter, destGridToCrs);
final MathTransform theMTSrcToDest = MathTransforms.concatenate(pixSrcGridToCrs, mt, pixDestGridToProjCrs.inverse());
/*
* Resampling
*/
final Resample resample = new Resample(theMTSrcToDest.inverse(), targetImage, sourceImg,
InterpolationCase.BICUBIC, ResampleBorderComportement.EXTRAPOLATION, new double[]{0});
resample.fillImage();
//-- get the grid
final ResampleGrid resGrid = resample.getGrid();
assertNotNull("grid should not be null", resGrid);
final double[] expectedResultByFeedBack = compareGrid(resGrid, 12, 12, theMTSrcToDest);
final Raster coverageRaster = targetImage.getTile(0, 0);
final double tol = StrictMath.sqrt(2) * 0.125;
java.awt.image.DataBufferDouble datadouble = (java.awt.image.DataBufferDouble) coverageRaster.getDataBuffer();
final double[] testedArray = datadouble.getData(0);
assertArrayEquals(expectedResultByFeedBack, testedArray, tol);
}
/**
* Study grid built during resample an verify pertinency of its values from
* destination coordinates transformed by {@link MathTransform}.
*
* @param testedGrid the grid which will be test.
* @param destImgWidth destination image width.
* @param destImgHeight destination image height.
* @param srcToDest {@link MathTransform} use to resample image.
* @throws TransformException
*/
private double[] compareGrid(final ResampleGrid testedGrid, final int destImgWidth, final int destImgHeight, final MathTransform srcToDest) throws TransformException {
final int gridW = testedGrid.getGridWidth();
final int gridH = testedGrid.getGridHeight();
final int stepx = testedGrid.getStepX();
final int stepy = testedGrid.getStepY();
final MathTransform destToSrc = srcToDest.inverse();
//-- interpolation value
final double[] feedBack = new double[destImgHeight * destImgWidth];
int fbid = 0;
final Interpolation interpol = Interpolation.create(PixelIteratorFactory.createDefaultIterator(sourceImg),
InterpolationCase.BICUBIC, 0, ResampleBorderComportement.EXTRAPOLATION, new double[]{0});
double[] grid = testedGrid.getGrid();
int desty = 0;
int destx = 0;
int gx = 0;
int gy = 0;
int nextgy = stepy;
double[] destCoords = new double[2];
while (desty < destImgHeight) {
if (desty == nextgy) {
nextgy += stepy;
gy++;
gy = StrictMath.min(gy, gridH - 2);
}
int nextgx = stepx;
destx = 0;
gx = 0;
while (destx < destImgWidth) {
if (destx == nextgx) {
nextgx += stepx;
gx++;
gx = StrictMath.min(gx, gridW - 2);
}
destCoords[0] = destx;
destCoords[1] = desty;
destToSrc.transform(destCoords, 0, destCoords, 0, 1);
feedBack[fbid++] = interpol.interpolate(destCoords[0], destCoords[1], 0);
// compare result entre result transformation et interpol grid[gx][gy]
final int id00 = ((gy * gridW + gx) << 1);
final int id01 = id00 + (gridW << 1);
final double testedx = interpolate2D(gx, gy, grid[id00], grid[id00 + 2], grid[id01], grid[id01 + 2], destx / ((double)stepx), desty / ((double)stepy));
final double testedy = interpolate2D(gx, gy, grid[id00 | 1], grid[(id00 + 2) | 1], grid[id01 | 1], grid[(id01 + 2) | 1], destx / ((double)stepx), desty / ((double)stepy));
assertEquals("at ("+destx+", "+desty+") X : ", destCoords[0], testedx, 0.125);
assertEquals("at ("+destx+", "+desty+") Y : ", destCoords[1], testedy, 0.125);
destx ++;
}
desty++;
}
return feedBack;
}
/**
* Bilinear interpolation.
*
* @param t0x bilinear interpolation origine in X direction.
* @param t0y bilinear interpolation origine in Y direction.
* @param f00
* @param f10
* @param f01
* @param f11
* @param x position which will be interpolate in X direction.
* @param y position which will be interpolate in Y direction.
* @return
*/
private static double interpolate2D(double t0x, double t0y, double f00, double f10, double f01, double f11, double x, double y) {
final double x0 = interpolate1D(t0x, x, f00, f10);
final double x1 = interpolate1D(t0x, x, f01, f11);
return interpolate1D(t0y, y, x0, x1);
}
/**
* Compute linear interpolation between 2 values.
* {@inheritDoc }
*/
private static double interpolate1D(double t0, double t, double f0, double f1) {
return (t - t0) * (f1 - f0) + f0;
}
/**
* Affect appropriate image for tests.
*
* @param width image width.
* @param height image height.
* @param dataType image data type.
* @param value fill image with this value.
*/
private void setTargetImage(int width, int height,
int dataType, double value) {
final ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
final ColorModel cm = new ComponentColorModel(cs, false, false, Transparency.OPAQUE, dataType);
final ImageTypeSpecifier imgTypeSpec = new ImageTypeSpecifier(cm, cm.createCompatibleSampleModel(1, 1));
targetImage = imgTypeSpec.createBufferedImage(width, height);
final PixelIterator pix = PixelIteratorFactory.createDefaultWriteableIterator(targetImage, targetImage);
while (pix.next()) {
pix.setSampleDouble(value);
}
}
/**
* Affect MathTransform with appropriate test values.
*
* @param mt
*/
private void setAffineMathTransform(MathTransform mt) {
mathTransform = mt;
}
}