/* JP2K Kakadu Image Writer V. 1.0
*
* (c) 2008 Quality Nighthawk Teleradiology Group, Inc.
* Contact: info@qualitynighthawk.com
*
* Produced by GeoSolutions, Eng. Daniele Romagnoli and Eng. Simone Giannecchini
* GeoSolutions S.A.S. --- Via Carignoni 51, 55041 Camaiore (LU) Italy
* Contact: info@geo-solutions.it
*
* Released under the Gnu Lesser General Public License version 3.
* All rights otherwise reserved.
*
* JP2K Kakadu Image Writer is distributed on an "AS IS" basis,
* WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU Lesser General Public License version 3 for more details.
* http://www.fsf.org/licensing/licenses/lgpl.html
*/
package it.geosolutions.imageio.plugins.jp2k;
import it.geosolutions.resources.TestData;
import it.geosolutions.util.KakaduUtilities;
import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.LinkedList;
import java.util.logging.Logger;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.media.jai.JAI;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.RenderedOp;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import kdu_jni.KduException;
import com.sun.imageio.plugins.bmp.BMPImageReaderSpi;
public class JP2KKakaduWriteTest extends TestCase {
/** The LOGGER for this class. */
private static final Logger LOGGER = Logger
.getLogger("it.geosolutions.imageio.plugins.jp2k");
public JP2KKakaduWriteTest(String name) {
super(name);
}
private static boolean isKakaduAvailable;
private static int writeOperations = 0;
private final static double lossLessQuality = 1;
private final static double lossyQuality = 0.125;
private final static String testPath;
private final static String FILE_SEPARATOR = System
.getProperty("file.separator");
/**
* Simple class to handle test configuration properties.
*/
class TestConfiguration {
String outputFileName;
boolean writeCodeStreamOnly;
double quality;
boolean useJAI;
JP2KKakaduImageWriteParam param = null;
public TestConfiguration(String fileName,
final boolean writeCodestreamOnly, final double quality,
final boolean useJAI, final JP2KKakaduImageWriteParam param) {
outputFileName = fileName;
this.writeCodeStreamOnly = writeCodestreamOnly;
this.quality = quality;
this.useJAI = useJAI;
this.param = param;
}
}
static {
try{
isKakaduAvailable = KakaduUtilities.isKakaduAvailable();
}
catch (UnsatisfiedLinkError ule){
isKakaduAvailable = false;
}
String path = System.getProperty("data.path");
if (path != null && path.length() > 1) {
path = path.replace("\\", "/");
final char lastChar = path.charAt(path.length() - 1);
if (lastChar == '/')
testPath = path;
else
testPath = path + "/";
} else
testPath = System.getProperty("java.io.tmpdir");
}
private final static String[] files = new String[] { };
// private final static String inputFileName = testPath;
private final static String outputFileName = testPath + FILE_SEPARATOR
+ "out";
public void testKakaduWriter() throws KduException, FileNotFoundException,
IOException {
if(!isKakaduAvailable){
LOGGER
.warning("Kakadu libs not found: test are skipped ");
return;
}
for (String fileName : files) {
// final String filePath = inputFileName + fileName;
// final File file = new File(filePath);
final File file = TestData.file(this, fileName);
final String filePath = file.getAbsolutePath();
if (!file.exists()) {
LOGGER
.warning("Unable to find the file "
+ filePath
// + "\n Be sure you have properly specified the \"data.path\" property linking to the location where test data is available."
+ "\n This test will be skipped");
continue;
}
else{
final String suffix = fileName.substring(0, fileName.length() - 4);
LinkedList<TestConfiguration> configs = new LinkedList<TestConfiguration>();
configs.add(new TestConfiguration(outputFileName + "_" + suffix,
true, lossLessQuality, false, null));
configs.add(new TestConfiguration(outputFileName + "_" + suffix,
false, lossLessQuality, false, null));
configs.add(new TestConfiguration(outputFileName + "_" + suffix,
true, lossyQuality, false, null));
configs.add(new TestConfiguration(outputFileName + "_" + suffix,
false, lossyQuality, false, null));
configs.add(new TestConfiguration(
outputFileName + "_JAI_" + suffix, true, lossLessQuality,
true, null));
configs.add(new TestConfiguration(
outputFileName + "_JAI_" + suffix, false, lossLessQuality,
true, null));
// configs.add(new TestConfiguration(
// outputFileName + "_JAI_" + suffix, true, lossyQuality,
// true, null));
// configs.add(new TestConfiguration(
// outputFileName + "_JAI_" + suffix, false, lossyQuality,
// true, null));
JP2KKakaduImageWriteParam param = new JP2KKakaduImageWriteParam();
final int levels = 2;
param.setCLevels(levels);
configs.add(new TestConfiguration(outputFileName + "_" + levels
+ "levels_" + suffix, true, lossLessQuality, false, param));
configs
.add(new TestConfiguration(outputFileName + "_" + levels
+ "levels_" + suffix, false, lossLessQuality,
false, param));
configs.add(new TestConfiguration(outputFileName + "_" + levels
+ "levels_" + suffix, true, lossyQuality, false, param));
configs.add(new TestConfiguration(outputFileName + "_" + levels
+ "levels_" + suffix, false, lossyQuality, false, param));
for (TestConfiguration config : configs) {
final ParameterBlockJAI pbjImageRead = new ParameterBlockJAI(
"ImageRead");
ImageReader reader = ImageIO.getImageReaders(
ImageIO.createImageInputStream(file)).next();
pbjImageRead.setParameter("reader", reader);
pbjImageRead.setParameter("Input", file);
RenderedOp image = JAI.create("ImageRead", pbjImageRead);
write(config.outputFileName, image, config.writeCodeStreamOnly,
config.quality, config.useJAI, config.param);
}
}
}
}
public void testKakaduWriterParam() throws KduException,
FileNotFoundException, IOException {
if(!isKakaduAvailable){
LOGGER
.warning("Kakadu libs not found: test are skipped ");
return;
}
if (files.length==0) {
LOGGER.warning("No files have been specified. This test will be skipped");
return;
}
final String fileName = files[0];
// final String filePath = inputFileName + fileName;
// final File file = new File(filePath);
final File file = TestData.file(this, fileName);
final String filePath = file.getAbsolutePath();
if (!file.exists()) {
LOGGER
.warning("Unable to find the file "
+ filePath
// + "\n Be sure you have properly specified the \"data.path\" property linking to the location where test data is available."
+ "\n This test will be skipped");
return;
}
final String suffix = fileName.substring(0, fileName.length() - 4);
LinkedList<TestConfiguration> configs = new LinkedList<TestConfiguration>();
JP2KKakaduImageWriteParam param = new JP2KKakaduImageWriteParam();
param.setSourceRegion(new Rectangle(100, 0, 450, 800));
param.setSourceSubsampling(2, 3, 0, 0);
configs.add(new TestConfiguration(outputFileName + "_pp_" + suffix,
true, lossLessQuality, false, param));
configs.add(new TestConfiguration(outputFileName + "_pp_" + suffix,
false, lossLessQuality, false, param));
configs.add(new TestConfiguration(outputFileName + "_pp_" + suffix,
true, lossyQuality, false, param));
configs.add(new TestConfiguration(outputFileName + "_pp_" + suffix,
false, lossyQuality, false, param));
configs.add(new TestConfiguration(outputFileName + "_pp_JAI_" + suffix,
true, lossLessQuality, true, param));
configs.add(new TestConfiguration(outputFileName + "_pp_JAI_" + suffix,
false, lossLessQuality, true, param));
// configs.add(new TestConfiguration(outputFileName + "_pp_JAI_" +
// suffix,
// true, lossyQuality, true, param));
// configs.add(new TestConfiguration(outputFileName + "_pp_JAI_" +
// suffix,
// false, lossyQuality, true, param));
for (TestConfiguration config : configs) {
final ParameterBlockJAI pbjImageRead = new ParameterBlockJAI(
"ImageRead");
ImageReader reader = ImageIO.getImageReaders(
ImageIO.createImageInputStream(file)).next();
pbjImageRead.setParameter("reader", reader);
pbjImageRead.setParameter("Input", file);
RenderedOp image = JAI.create("ImageRead", pbjImageRead);
write(config.outputFileName, image, config.writeCodeStreamOnly,
config.quality, config.useJAI, config.param);
}
}
private static synchronized void write(String file, RenderedImage bi,
boolean codeStreamOnly, double quality, boolean useJAI,
JP2KKakaduImageWriteParam addParam) throws IOException {
writeOperations++;
file += "_Q" + quality + (codeStreamOnly ? ".j2c" : ".jp2");
final ImageOutputStream outputStream = ImageIO
.createImageOutputStream(new File(file));
JP2KKakaduImageWriteParam param = new JP2KKakaduImageWriteParam();
param.setQuality(quality);
param.setWriteCodeStreamOnly(codeStreamOnly);
if (addParam != null) {
param.setSourceRegion(addParam.getSourceRegion());
param.setSourceSubsampling(addParam.getSourceXSubsampling(),
addParam.getSourceYSubsampling(), addParam
.getSubsamplingXOffset(), addParam
.getSubsamplingYOffset());
param.setCLevels(addParam.getCLevels());
param.setQualityLayers(addParam.getQualityLayers());
}
if (!useJAI) {
final ImageWriter writer = new JP2KKakaduImageWriterSpi()
.createWriterInstance();
// final ImageWriter writer = new
// J2KImageWriterSpi().createWriterInstance();
writer.setOutput(outputStream);
// J2KImageWriteParam ioparam = (J2KImageWriteParam)
// writer.getDefaultWriteParam();
// ioparam.setWriteCodeStreamOnly(true);
// ioparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
// ioparam.setCompressionType("JPEG2000");
// ioparam.setCompressionQuality((float)quality);
// ioparam.setEncodingRate((quality)*24);
// writer.write(null, new IIOImage(bi, null, null), ioparam);
writer.write(null, new IIOImage(bi, null, null), param);
writer.dispose();
} else {
final ParameterBlockJAI pbjImageWrite = new ParameterBlockJAI(
"ImageWrite");
final ImageWriter writer = new JP2KKakaduImageWriterSpi()
.createWriterInstance();
pbjImageWrite.setParameter("writer", writer);
pbjImageWrite.setParameter("output", outputStream);
pbjImageWrite.setParameter("writeParam", param);
pbjImageWrite.addSource(bi);
RenderedOp image = JAI.create("ImageWrite", pbjImageWrite);
}
}
public static void main(java.lang.String[] args) {
junit.textui.TestRunner.run(suite());
LOGGER.info(writeOperations + " write operations performed");
}
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTest(new JP2KKakaduWriteTest("testKakaduWriter"));
suite.addTest(new JP2KKakaduWriteTest("testKakaduWriterParam"));
suite.addTest(new JP2KKakaduWriteTest("testRGB"));
suite.addTest(new JP2KKakaduWriteTest("test8BitGray"));
suite.addTest(new JP2KKakaduWriteTest("test12BitGray"));
suite.addTest(new JP2KKakaduWriteTest("test16BitGray"));
suite.addTest(new JP2KKakaduWriteTest("test24BitGray"));
suite.addTest(new JP2KKakaduWriteTest("testPalettedRGB"));
suite.addTest(new JP2KKakaduWriteTest("testReducedMemory"));
suite.addTest(new JP2KKakaduWriteTest("testOutputStream"));
return suite;
}
public static void testReducedMemory() throws IOException {
if(!isKakaduAvailable){
LOGGER
.warning("Kakadu libs not found: test are skipped ");
return;
}
System.setProperty(JP2KKakaduImageWriter.MAX_BUFFER_SIZE_KEY, "64K");
final ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorModel cm = new ComponentColorModel(cs, new int[] { 16 }, false,
false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
final int w = 512;
final int h = 512;
SampleModel sm = cm.createCompatibleSampleModel(w, h);
final int bufferSize = w * h;
final short[] bufferValues = new short[bufferSize];
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++)
// bufferValues[j + (i * h)] = (short) ((j + i) * (65536 /
// 1024));
bufferValues[j + (i * h)] = (short) (Math.random() * 65535);
}
DataBuffer imageBuffer = new DataBufferUShort(bufferValues, bufferSize);
BufferedImage bi = new BufferedImage(cm, Raster.createWritableRaster(
sm, imageBuffer, null), false, null);
write(outputFileName + "_RM", bi, true, lossLessQuality);
write(outputFileName + "_RM", bi, false, lossLessQuality);
write(outputFileName + "_RM", bi, true, lossyQuality);
write(outputFileName + "_RM", bi, false, lossyQuality);
LOGGER.info(writeOperations + " write operations performed");
}
public static void test8BitGray() throws IOException {
if(!isKakaduAvailable){
LOGGER
.warning("Kakadu libs not found: test are skipped ");
return;
}
final ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorModel cm = new ComponentColorModel(cs, new int[] { 8 }, false,
false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
final int w = 128;
final int h = 128;
SampleModel sm = cm.createCompatibleSampleModel(w, h);
final int bufferSize = w * h;
final byte[] bufferValues = new byte[bufferSize];
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++)
// bufferValues[j + (i * h)] = (short) ((j + i) * (4096 /
// 1024));
bufferValues[j + (i * h)] = (byte) (Math.random() * 255d);
}
DataBuffer imageBuffer = new DataBufferByte(bufferValues, bufferSize);
BufferedImage bi = new BufferedImage(cm, Raster.createWritableRaster(
sm, imageBuffer, null), false, null);
write(outputFileName + "_gray8", bi, true, lossLessQuality);
write(outputFileName + "_gray8", bi, false, lossLessQuality);
write(outputFileName + "_gray8", bi, true, lossyQuality);
write(outputFileName + "_gray8", bi, false, lossyQuality);
write(outputFileName + "_JAI_gray8", bi, true, lossLessQuality, true);
write(outputFileName + "_JAI_gray8", bi, false, lossLessQuality, true);
// write(outputFileName + "_JAI_gray12", bi, true, lossyQuality, true);
// write(outputFileName + "_JAI_gray12", bi, false, lossyQuality, true);
LOGGER.info(writeOperations + " write operations performed");
}
public static void test12BitGray() throws IOException {
if(!isKakaduAvailable){
LOGGER
.warning("Kakadu libs not found: test are skipped ");
return;
}
final ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorModel cm = new ComponentColorModel(cs, new int[] { 12 }, false,
false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
final int w = 512;
final int h = 512;
SampleModel sm = cm.createCompatibleSampleModel(w, h);
final int bufferSize = w * h;
final short[] bufferValues = new short[bufferSize];
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++)
// bufferValues[j + (i * h)] = (short) ((j + i) * (4096 /
// 1024));
bufferValues[j + (i * h)] = (short) (Math.random() * 4095d);
}
DataBuffer imageBuffer = new DataBufferUShort(bufferValues, bufferSize);
BufferedImage bi = new BufferedImage(cm, Raster.createWritableRaster(
sm, imageBuffer, null), false, null);
write(outputFileName + "_gray12", bi, true, lossLessQuality);
write(outputFileName + "_gray12", bi, false, lossLessQuality);
write(outputFileName + "_gray12", bi, true, lossyQuality);
write(outputFileName + "_gray12", bi, false, lossyQuality);
write(outputFileName + "_JAI_gray12", bi, true, lossLessQuality, true);
write(outputFileName + "_JAI_gray12", bi, false, lossLessQuality, true);
// write(outputFileName + "_JAI_gray12", bi, true, lossyQuality, true);
// write(outputFileName + "_JAI_gray12", bi, false, lossyQuality, true);
LOGGER.info(writeOperations + " write operations performed");
}
public static void test16BitGray() throws IOException {
if(!isKakaduAvailable){
LOGGER
.warning("Kakadu libs not found: test are skipped ");
return;
}
final ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorModel cm = new ComponentColorModel(cs, new int[] { 16 }, false,
false, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
final int w = 512;
final int h = 512;
SampleModel sm = cm.createCompatibleSampleModel(w, h);
final int bufferSize = w * h;
final short[] bufferValues = new short[bufferSize];
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++)
// bufferValues[j + (i * h)] = (short) ((j + i) * (65536 /
// 1024));
bufferValues[j + (i * h)] = (short) (Math.random() * 65535);
}
DataBuffer imageBuffer = new DataBufferUShort(bufferValues, bufferSize);
BufferedImage bi = new BufferedImage(cm, Raster.createWritableRaster(
sm, imageBuffer, null), false, null);
JP2KKakaduImageWriteParam param = new JP2KKakaduImageWriteParam();
param.setSourceSubsampling(2, 3, 0, 0);
write(outputFileName + "_gray16", bi, true, lossLessQuality);
write(outputFileName + "_gray16", bi, false, lossLessQuality);
write(outputFileName + "_gray16", bi, true, lossyQuality);
write(outputFileName + "_gray16", bi, false, lossyQuality);
write(outputFileName + "_JAI_gray16", bi, true, lossLessQuality, true);
write(outputFileName + "_JAI_gray16", bi, false, lossLessQuality, true);
write(outputFileName + "_JAI_subSampled_gray16", bi, true,
lossyQuality, true, param);
write(outputFileName + "_JAI_subSampled_gray16", bi, false,
lossyQuality, true, param);
LOGGER.info(writeOperations + " write operations performed");
}
public static void test24BitGray() throws IOException {
if(!isKakaduAvailable){
LOGGER
.warning("Kakadu libs not found: test are skipped ");
return;
}
final ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorModel cm = new ComponentColorModel(cs, new int[] { 24 }, false,
false, Transparency.OPAQUE, DataBuffer.TYPE_INT);
final int w = 512;
final int h = 512;
SampleModel sm = cm.createCompatibleSampleModel(w, h);
final int bufferSize = w * h;
final int[] bufferValues = new int[bufferSize];
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++)
// bufferValues[j + (i * h)] = (int) (j + i) * (16777216 /
// 1024);
bufferValues[j + (i * h)] = (int) (Math.random() * 16777215d);
}
DataBuffer imageBuffer = new DataBufferInt(bufferValues, bufferSize);
BufferedImage bi = new BufferedImage(cm, Raster.createWritableRaster(
sm, imageBuffer, null), false, null);
JP2KKakaduImageWriteParam param = new JP2KKakaduImageWriteParam();
param.setSourceSubsampling(2, 3, 0, 0);
write(outputFileName + "_gray24", bi, true, lossLessQuality);
write(outputFileName + "_gray24", bi, false, lossLessQuality);
write(outputFileName + "_gray24", bi, true, lossyQuality);
write(outputFileName + "_gray24", bi, false, lossyQuality);
write(outputFileName + "_JAI_gray24", bi, true, lossLessQuality, true);
write(outputFileName + "_JAI_gray24", bi, false, lossLessQuality, true);
write(outputFileName + "_JAI_subSampled_gray24", bi, true,
lossyQuality, true, param);
write(outputFileName + "_JAI_subSampled_gray24", bi, false,
lossyQuality, true, param);
LOGGER.info(writeOperations + " write operations performed");
}
public void testOutputStream() throws IOException {
if(!isKakaduAvailable){
LOGGER
.warning("Kakadu libs not found: test are skipped ");
return;
}
final File outFile = File.createTempFile("stream", "temp");
final FileImageOutputStream stream = new FileImageOutputStream (outFile);
stream.writeBytes("This is an Image Header written before the j2c raw codestream");
final ImageReader reader = new BMPImageReaderSpi()
.createReaderInstance();
final File file = TestData.file(this, "RGB24.bmp");
reader.setInput(ImageIO.createImageInputStream(file));
BufferedImage bi = reader.read(0);
final ImageWriter writer = new JP2KKakaduImageWriterSpi()
.createWriterInstance();
JP2KKakaduImageWriteParam param = new JP2KKakaduImageWriteParam();
param.setQuality(0.8);
param.setWriteCodeStreamOnly(true);
writer.setOutput(stream);
writer.write(null, new IIOImage(bi, null, null), param);
writer.dispose();
LOGGER.info(writeOperations + " write operations performed");
}
public void testRGB() throws IOException {
if(!isKakaduAvailable){
LOGGER
.warning("Kakadu libs not found: test are skipped ");
return;
}
final File file = TestData.file(this, "RGB24.bmp");
final ImageReader reader = new BMPImageReaderSpi()
.createReaderInstance();
reader.setInput(ImageIO.createImageInputStream(file));
BufferedImage bi = reader.read(0);
write(outputFileName + "_RGB", bi, true, lossLessQuality);
write(outputFileName + "_RGB", bi, false, lossLessQuality);
write(outputFileName + "_RGB", bi, true, lossyQuality);
write(outputFileName + "_RGB", bi, false, lossyQuality);
write(outputFileName + "_JAI_RGB", bi, true, lossLessQuality, true);
write(outputFileName + "_JAI_RGB", bi, false, lossLessQuality, true);
// write(outputFileName + "_JAI_RGB", bi, true, lossyQuality, true);
// write(outputFileName + "_JAI_RGB", bi, false, lossyQuality, true);
LOGGER.info(writeOperations + " write operations performed");
}
public void testPalettedRGB() throws IOException {
if(!isKakaduAvailable){
LOGGER
.warning("Kakadu libs not found: test are skipped ");
return;
}
// BufferedImage bi = ImageIO.read(TestData.file(this, "paletted.tif"));
// write(outputFileName + "_RGB8", bi, true, lossLessQuality);
// write(outputFileName + "_RGB8", bi, false, lossLessQuality);
// write(outputFileName + "_JAI_RGB8", bi, true, lossLessQuality, true);
// write(outputFileName + "_JAI_RGB8", bi, false, lossLessQuality, true);
// LOGGER.info(writeOperations + " write operations performed");
}
private static void write(String file, final RenderedImage bi,
final boolean codeStreamOnly, final double quality)
throws IOException {
write(file, bi, codeStreamOnly, quality, false);
}
private static void write(String file, final RenderedImage bi,
final boolean codeStreamOnly, final double quality,
final boolean useJAI) throws IOException {
write(file, bi, codeStreamOnly, quality, useJAI, null);
}
}