/* JAI-Ext - OpenSource Java Advanced Image Extensions Library
* http://www.geo-solutions.it/
* Copyright 2014 GeoSolutions
* 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 it.geosolutions.jaiext.affine;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.DataBuffer;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import it.geosolutions.jaiext.JAIExt;
import it.geosolutions.jaiext.interpolators.InterpolationBicubic;
import it.geosolutions.jaiext.interpolators.InterpolationBilinear;
import it.geosolutions.jaiext.interpolators.InterpolationNearest;
import it.geosolutions.jaiext.range.Range;
import it.geosolutions.jaiext.range.RangeFactory;
import it.geosolutions.jaiext.testclasses.TestData;
import javax.media.jai.BorderExtender;
import javax.media.jai.Interpolation;
import javax.media.jai.JAI;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.RenderedOp;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* This test class is used for compare the timing between the new Nearest,Bilinear and Bicubic interpolators and their JAI version on the affine
* operation. No Roi or No Data range are used. If the user wants to change the number of the benchmark cycles or of the not benchmark cycles, should
* only pass the new values to the JAI.Ext.BenchmarkCycles or JAI.Ext.NotBenchmarkCycles parameters. The tests are quite different because the
* interpolator used can be one of the 3 JAI interpolators but the other operations are similar:
* <ul>
* <li>Selection of the descriptor (new or old AffineDescriptor)</li>
* <li>Image Transformation</li>
* <li>statistic calculation (if the cycle belongs to the benchmark cycles).</li>
* </ul>
* The selection of the old or new descriptor must be done by setting to true or false the JVM parameter JAI.Ext.OldDescriptor. The interpolator can
* be chosen by passing the JAI.Ext.TestSelector Integer JVM parameter: 0 for nearest interpolation, 1 for bilinear, 2 for bicubic. The transformation
* used is selected by passing the JVM integral parameter JAI.Ext.TransformationSelector, with 0 that indicates rotation, 1 scale, 2 combination of
* them. If the user wants to use the accelerated code, the JVM parameter JAI.Ext.Acceleration must be set to true.
*/
public class ComparisonTest extends TestAffine{
/** Number of benchmark iterations (Default 1) */
private final static int BENCHMARK_ITERATION = Integer.getInteger("JAI.Ext.BenchmarkCycles", 1);
/** Number of not benchmark iterations (Default 0) */
private final static int NOT_BENCHMARK_ITERATION = Integer.getInteger(
"JAI.Ext.NotBenchmarkCycles", 0);
/** Boolean indicating if the old descriptor must be used */
private final static boolean OLD_DESCRIPTOR = Boolean.getBoolean("JAI.Ext.OldDescriptor");
/** Boolean indicating if the native acceleration must be used */
private final static boolean NATIVE_ACCELERATION = Boolean.getBoolean("JAI.Ext.Acceleration");
/** Integer indicating which operation should be used (Default 0) */
public static Integer TRANSFORMATION_SELECTOR = Integer.getInteger(
"JAI.Ext.TransformationSelector", 0);
/** Index for selecting one of the 3 interpolators(Default 0) */
private final static int TEST_SELECTOR = Integer.getInteger("JAI.Ext.TestSelector", 0);
/** Boolean indicating if a No Data Range must be used */
private final static boolean RANGE_USED = Boolean.getBoolean("JAI.Ext.RangeUsed");
/** Default subsampling bits used for the bilinear and bicubic interpolation */
private final static int DEFAULT_SUBSAMPLE_BITS = 8;
/** Default precision bits used in the bicubic calculation */
private final static int DEFAULT_PRECISION_BITS = 8;
/** Value indicating No Data for the destination image */
private static double destinationNoData = 0;
/** Rotation used */
private static AffineTransform rotateTransform;
/** Translation used */
private static AffineTransform translateTransform;
/** Scale used */
private static AffineTransform scaleTransform;
/** JAI nearest Interpolator */
private static javax.media.jai.InterpolationNearest interpNearOld;
/** New nearest Interpolator */
private static InterpolationNearest interpNearNew;
/** JAI bilinear Interpolator */
private static javax.media.jai.InterpolationBilinear interpBilOld;
/** New bilinear Interpolator */
private static InterpolationBilinear interpBilNew;
/** JAI bicubic Interpolator */
private static javax.media.jai.InterpolationBicubic interpBicOld;
/** New bicubic Interpolator */
private static InterpolationBicubic interpBicNew;
/** Image to elaborate */
private static RenderedImage image;
/** RenderingHints used for selecting the borderExtender */
private static RenderingHints hints;
private static int[][] weight;
@BeforeClass
public static void initialSetup() throws FileNotFoundException, IOException {
int dataType = DataBuffer.TYPE_BYTE;
// Range creation if selected
Range rangeND = null;
if (RANGE_USED) {
rangeND = RangeFactory.create((byte) 100, true, (byte) 100, true);
}
// Interpolators instantiation
interpNearOld = new javax.media.jai.InterpolationNearest();
interpNearNew = new InterpolationNearest(rangeND, false, destinationNoData, dataType);
interpBilOld = new javax.media.jai.InterpolationBilinear(DEFAULT_SUBSAMPLE_BITS);
interpBilNew = new InterpolationBilinear(DEFAULT_SUBSAMPLE_BITS, rangeND, false,
destinationNoData, dataType);
interpBicOld = new javax.media.jai.InterpolationBicubic(DEFAULT_SUBSAMPLE_BITS);
interpBicNew = new InterpolationBicubic(DEFAULT_SUBSAMPLE_BITS, rangeND, false,
destinationNoData, dataType, false, DEFAULT_PRECISION_BITS);
// Selection of the RGB image
ParameterBlockJAI pbj = new ParameterBlockJAI("ImageRead");
// String file =
// "../jt-utilities/src/test/resources/it/geosolutions/jaiext/images/testImageLittle.tif";
File file = TestData.file(ComparisonTest.class, "testImageLittle.tif");
pbj.setParameter("Input", file);
image = JAI.create("ImageRead", pbj);
hints = new RenderingHints(JAI.KEY_BORDER_EXTENDER,
BorderExtender.createInstance(BorderExtender.BORDER_COPY));
// 45� degrees rotation
double theta = Math.PI / 4;
rotateTransform = AffineTransform.getRotateInstance(theta);
// 100 px translation
translateTransform = AffineTransform.getTranslateInstance(100, 0);
// 2 x scale
scaleTransform = AffineTransform.getScaleInstance(1.5f, 1.5f);
weight = new int[4][BENCHMARK_ITERATION + NOT_BENCHMARK_ITERATION];
for (int i = 0; i < BENCHMARK_ITERATION + NOT_BENCHMARK_ITERATION; i++) {
double rnd = Math.random();
if (rnd >= 0 & rnd < 0.25d) {
weight[0][i] = 0;
weight[1][i] = 1;
weight[2][i] = 0;
weight[3][i] = 0;
} else if (rnd >= 0.25d & rnd < 0.5d) {
weight[0][i] = 0;
weight[1][i] = 1;
weight[2][i] = 0;
weight[3][i] = 1;
} else if (rnd >= 0.25d & rnd < 0.5d) {
weight[0][i] = 1;
weight[1][i] = 1;
weight[2][i] = 0;
weight[3][i] = 0;
} else {
weight[0][i] = 1;
weight[1][i] = 1;
weight[2][i] = 1;
weight[3][i] = 1;
}
}
if(OLD_DESCRIPTOR){
JAIExt.registerJAIDescriptor("Affine");
}
}
@Test
public void testSpeedCalculationInt() {
// shift and round defined
// Internal precision required for position calculations
int one = 1 << DEFAULT_SUBSAMPLE_BITS;
// Subsampling related variables
int shift2 = 2 * DEFAULT_SUBSAMPLE_BITS;
int round2 = 1 << (shift2 - 1);
int s0 = 0;
int s1 = 0;
int s = 0;
int s00 = 0;
int s01 = 1;
int s10 = 2;
int s11 = 3;
int xfrac = (int) (Math.random() * Math.pow(2, DEFAULT_SUBSAMPLE_BITS));
int yfrac = (int) (Math.random() * Math.pow(2, DEFAULT_SUBSAMPLE_BITS));
long mean = 0;
long max = Long.MIN_VALUE;
long min = Long.MAX_VALUE;
for (int i = 0; i < BENCHMARK_ITERATION + NOT_BENCHMARK_ITERATION; i++) {
int w00 = weight[0][i];
int w01 = weight[1][i];
int w10 = weight[2][i];
int w11 = weight[3][i];
// Boolean indicating if a pixel weight is 0
boolean w00z = w00 == 0;
boolean w01z = w01 == 0;
boolean w10z = w10 == 0;
boolean w11z = w11 == 0;
// Total calculation time
long start = System.nanoTime();
// Complementary values of the fractional part
int xfracCompl = one - xfrac;
int yfracCompl = one - yfrac;
// Interpolation for type byte, ushort, short
if (w00z && w01z) {
s0 = 0;
} else if (w00z) { // w01 = 1
s0 = -s01 * xfracCompl + (s01 << DEFAULT_SUBSAMPLE_BITS);
} else if (w01z) {// w00 = 1
s0 = -s00 * xfrac + (s00 << DEFAULT_SUBSAMPLE_BITS);// s00;
} else {// w00 = 1 & W01 = 1
s0 = (s01 - s00) * xfrac + (s00 << DEFAULT_SUBSAMPLE_BITS);
}
// lower value
if (w10z && w11z) {
s1 = 0;
} else if (w10z) { // w11 = 1
s1 = -s11 * xfracCompl + (s11 << DEFAULT_SUBSAMPLE_BITS);
} else if (w11z) { // w10 = 1
s1 = -s10 * xfrac + (s10 << DEFAULT_SUBSAMPLE_BITS);// - (s10 * xfrac); //s10;
} else {
s1 = (s11 - s10) * xfrac + (s10 << DEFAULT_SUBSAMPLE_BITS);
}
if (w00z && w01z) {
s = (-s1 * yfracCompl + (s1 << DEFAULT_SUBSAMPLE_BITS) + round2) >> shift2;
} else {
if (w10z && w11z) {
s = (-s0 * yfrac + (s0 << DEFAULT_SUBSAMPLE_BITS) + round2) >> shift2;
} else {
s = ((s1 - s0) * yfrac + (s0 << DEFAULT_SUBSAMPLE_BITS) + round2) >> shift2;
}
}
long end = System.nanoTime() - start;
// If the the first NOT_BENCHMARK_ITERATION cycles has been done, then the mean, maximum and minimum values are stored
if (i > NOT_BENCHMARK_ITERATION - 1) {
if (i == NOT_BENCHMARK_ITERATION) {
mean = end;
} else {
mean = mean + end;
}
if (end > max) {
max = end;
}
if (end < min) {
min = end;
}
}
}
// Mean values
double meanValue = mean / BENCHMARK_ITERATION;
// Max and Min values stored as double
double maxD = max;
double minD = min;
// Output print
System.out.println("\nMean value for int calculation : " + meanValue + " nsec.");
System.out.println("Maximum value for int calculation : " + maxD + " nsec.");
System.out.println("Minimum value for int calculation : " + minD + " nsec.");
}
@Test
public void testSpeedCalculationDouble() {
double s0 = 0;
double s1 = 0;
double s = 0;
double s00 = 0;
double s01 = 1;
double s10 = 2;
double s11 = 3;
float xfrac = (float) Math.random();
float yfrac = (float) Math.random();
// Complementary values of the fractional part
float xfracCompl = 1 - xfrac;
float yfracCompl = 1 - yfrac;
long mean = 0;
long max = Long.MIN_VALUE;
long min = Long.MAX_VALUE;
for (int i = 0; i < BENCHMARK_ITERATION + NOT_BENCHMARK_ITERATION; i++) {
int w00 = weight[0][i];
int w01 = weight[1][i];
int w10 = weight[2][i];
int w11 = weight[3][i];
// Boolean indicating if a pixel weight is 0
boolean w00z = w00 == 0;
boolean w01z = w01 == 0;
boolean w10z = w10 == 0;
boolean w11z = w11 == 0;
// Total calculation time
long start = System.nanoTime();
if (w00z || w01z || w10z || w11z) {
if (w00z && w01z) {
s0 = 0;
} else if (w00z) { // w01 = 1
s0 = s01 * xfrac;
} else if (w01z) {// w00 = 1
s0 = s00 * xfracCompl;// s00;
} else {// w00 = 1 & W01 = 1
s0 = (s01 - s00) * xfrac + s00;
}
// lower value
if (w10z && w11z) {
s1 = 0;
} else if (w10z) { // w11 = 1
s1 = s11 * xfrac;
} else if (w11z) { // w10 = 1
s1 = s10 * xfracCompl;// - (s10 * xfrac); //s10;
} else {
s1 = (s11 - s10) * xfrac + s10;
}
if (w00z && w01z) {
s = s1 * yfrac;
} else {
if (w10z && w11z) {
s = s0 * yfracCompl;
} else {
s = (s1 - s0) * yfrac + s0;
}
}
} else {
// Perform the bilinear interpolation because all the weight are not 0.
s0 = (s01 - s00) * xfrac + s00;
s1 = (s11 - s10) * xfrac + s10;
s = (s1 - s0) * yfrac + s0;
}
long end = System.nanoTime() - start;
// If the the first NOT_BENCHMARK_ITERATION cycles has been done, then the mean, maximum and minimum values are stored
if (i > NOT_BENCHMARK_ITERATION - 1) {
if (i == NOT_BENCHMARK_ITERATION) {
mean = end;
} else {
mean = mean + end;
}
if (end > max) {
max = end;
}
if (end < min) {
min = end;
}
}
}
// Mean values
double meanValue = mean / BENCHMARK_ITERATION;
// Max and Min values stored as double
double maxD = max;
double minD = min;
// Output print
System.out.println("\nMean value for double calculation : " + meanValue + " nsec.");
System.out.println("Maximum value for double calculation : " + maxD + " nsec.");
System.out.println("Minimum value for double calculation : " + minD + " nsec.");
}
@Test
public void testNearestNewAffineDescriptor() {
if (TEST_SELECTOR == 0 && !OLD_DESCRIPTOR) {
testInterpolators(interpNearNew, OLD_DESCRIPTOR);
}
}
@Test
public void testNearestOldAffineDescriptor() {
if (TEST_SELECTOR == 0 && OLD_DESCRIPTOR) {
testInterpolators(interpNearOld, OLD_DESCRIPTOR);
}
}
@Test
public void testBilinearNewAffineDescriptor() {
if (TEST_SELECTOR == 1 && !OLD_DESCRIPTOR) {
testInterpolators(interpBilNew, OLD_DESCRIPTOR);
}
}
@Test
public void testBilinearOldAffineDescriptor() {
if (TEST_SELECTOR == 1 && OLD_DESCRIPTOR) {
testInterpolators(interpBilOld, OLD_DESCRIPTOR);
}
}
@Test
public void testBicubicNewAffineDescriptor() {
if (TEST_SELECTOR == 2 && !OLD_DESCRIPTOR) {
testInterpolators(interpBicNew, OLD_DESCRIPTOR);
}
}
@Test
public void testBicubicOldAffineDescriptor() {
if (TEST_SELECTOR == 2 && OLD_DESCRIPTOR) {
testInterpolators(interpBicOld, OLD_DESCRIPTOR);
}
}
public void testInterpolators(Interpolation interp, boolean old) {
String interpType = "";
if (interp instanceof javax.media.jai.InterpolationBilinear
|| interp instanceof InterpolationBilinear) {
interpType = "Bilinear";
} else if (interp instanceof javax.media.jai.InterpolationBicubic
|| interp instanceof InterpolationBicubic) {
interpType = "Bicubic";
} else if (interp instanceof javax.media.jai.InterpolationNearest
|| interp instanceof InterpolationNearest) {
interpType = "Nearest";
}
String description = "";
if (old) {
description = "Old Affine";
if (NATIVE_ACCELERATION) {
description += " accelerated ";
System.setProperty("com.sun.media.jai.disableMediaLib", "false");
} else {
System.setProperty("com.sun.media.jai.disableMediaLib", "true");
}
} else {
description = "New Affine";
System.setProperty("com.sun.media.jai.disableMediaLib", "true");
}
AffineTransform transform = new AffineTransform();
switch (TRANSFORMATION_SELECTOR) {
case 0:
transform.concatenate(rotateTransform);
break;
case 1:
transform.concatenate(scaleTransform);
break;
case 2:
transform.concatenate(rotateTransform);
transform.concatenate(scaleTransform);
transform.concatenate(translateTransform);
break;
default:
throw new IllegalArgumentException("Wrong transformation value");
}
// Destination no data used by the affine operation with the classic
// bilinear interpolator
double[] destinationNoDataArray = { destinationNoData, destinationNoData, destinationNoData };
// Total cycles number
int totalCycles = BENCHMARK_ITERATION + NOT_BENCHMARK_ITERATION;
// Image with the interpolator
PlanarImage imageAffine = null;
long mean = 0;
long max = Long.MIN_VALUE;
long min = Long.MAX_VALUE;
// Cycle for calculating the mean, maximum and minimum calculation time
for (int i = 0; i < totalCycles; i++) {
// creation of the image with the selected interpolator
if (old) {
imageAffine = javax.media.jai.operator.AffineDescriptor.create(image, transform,
interp, destinationNoDataArray, hints);
} else {
imageAffine = AffineDescriptor.create(image, transform, interp,
destinationNoDataArray, null, false, false, null, hints);
}
// Total calculation time
long start = System.nanoTime();
imageAffine.getTiles();
long end = System.nanoTime() - start;
// If the the first NOT_BENCHMARK_ITERATION cycles has been done,
// then the mean, maximum and minimum values are stored
if (i > NOT_BENCHMARK_ITERATION - 1) {
if (i == NOT_BENCHMARK_ITERATION) {
mean = end;
} else {
mean = mean + end;
}
if (end > max) {
max = end;
}
if (end < min) {
min = end;
}
}
// For every cycle the cache is flushed such that all the tiles must
// be recalculates
JAI.getDefaultInstance().getTileCache().flush();
}
// Mean values
double meanValue = mean / BENCHMARK_ITERATION * 1E-6;
// Max and Min values stored as double
double maxD = max * 1E-6;
double minD = min * 1E-6;
// Comparison between the mean times
// Output print of the
System.out.println("\n" + interpType);
System.out.println("\nMean value for " + description + "Descriptor : " + meanValue
+ " msec.");
System.out.println("Maximum value for " + description + "Descriptor : " + maxD + " msec.");
System.out.println("Minimum value for " + description + "Descriptor : " + minD + " msec.");
// Final Image disposal
if (imageAffine instanceof RenderedOp) {
((RenderedOp) imageAffine).dispose();
}
}
}