/*
* Copyright (c) 2008 Los Alamos National Security, LLC.
*
* Los Alamos National Laboratory
* Research Library
* Digital Library Research & Prototyping Team
*
* 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
package gov.lanl.adore.djatoka.kdu;
import gov.lanl.adore.djatoka.DjatokaEncodeParam;
import gov.lanl.adore.djatoka.DjatokaException;
import gov.lanl.adore.djatoka.ICompress;
import gov.lanl.adore.djatoka.util.IOUtils;
import gov.lanl.adore.djatoka.util.ImageProcessingUtils;
import gov.lanl.adore.djatoka.util.ImageRecord;
import gov.lanl.adore.djatoka.util.ImageRecordUtils;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.log4j.Logger;
/**
* Java bridge for kdu_compress application
* @author Ryan Chute
*
*/
public class KduCompressExe implements ICompress {
static Logger logger = Logger.getLogger(KduCompressExe.class);
private static boolean isWindows = false;
private static String env;
private static String exe;
private static String[] envParams;
/** Compress App Name "kdu_compress" */
public static final String KDU_COMPRESS_EXE = "kdu_compress";
/** UNIX/Linux Standard Out Path: "/dev/stdout" */
public static String STDOUT = "/dev/stdout";
static {
env = System.getProperty("kakadu.home")
+ System.getProperty("file.separator");
exe = env
+ ((System.getProperty("os.name").startsWith("Win")) ? KDU_COMPRESS_EXE
+ ".exe"
: KDU_COMPRESS_EXE);
if (System.getProperty("os.name").startsWith("Mac")) {
envParams = new String[] { "DYLD_LIBRARY_PATH="
+ System.getProperty("DYLD_LIBRARY_PATH") };
} else if (System.getProperty("os.name").startsWith("Win")) {
isWindows = true;
} else if (System.getProperty("os.name").startsWith("Linux")) {
envParams = new String[] { "LD_LIBRARY_PATH="
+ System.getProperty("LD_LIBRARY_PATH") };
} else if (System.getProperty("os.name").startsWith("Solaris")) {
envParams = new String[] { "LD_LIBRARY_PATH="
+ System.getProperty("LD_LIBRARY_PATH") };
}
logger.debug("envParams: " + ((envParams != null) ? envParams[0] + " | " : "") + exe );
}
/**
* Constructor which expects the following system properties to be defined and exported.
* <p/>kakadu.home=$DJATOKA_HOME/bin/$DJATOKA_OS
* <p/>(Win/Linux/UNIX) LD_LIBRARY_PATH=$DJATOKA_HOME/lib/$DJATOKA_OS
* <p/>(Mac OS-X) DYLD_LIBRARY_PATH=$DJATOKA_HOME/lib/$DJATOKA_OS
* @throws Exception
*/
public KduCompressExe() throws Exception {
env = System.getProperty("kakadu.home");
if (env == null) {
logger.error("kakadu.home is not defined");
System.err.println("kakadu.home is not defined");
throw new Exception("kakadu.home is not defined");
}
}
/**
* Compress input BufferedImage using provided DjatokaEncodeParam parameters.
* @param bi in-memory image to be compressed
* @param output absolute file path for output file.
* @param params DjatokaEncodeParam containing compression parameters.
* @throws DjatokaException
*/
public void compressImage(BufferedImage bi, String output,
DjatokaEncodeParam params) throws DjatokaException {
if (params == null)
params = new DjatokaEncodeParam();
if (params.getLevels() == 0)
params.setLevels(ImageProcessingUtils.getLevelCount(bi.getWidth(), bi.getHeight()));
File in = null;
try {
in = IOUtils.createTempTiff(bi);
compressImage(in.getAbsolutePath(), output, params);
} catch (IOException e) {
logger.error(e,e);
throw new DjatokaException(e);
} catch (Exception e) {
logger.error(e,e);
throw new DjatokaException(e);
} finally {
if (in != null)
in.delete();
}
}
/**
* Compress input BufferedImage using provided DjatokaEncodeParam parameters.
* @param bi in-memory image to be compressed
* @param output OutputStream to serialize compressed image.
* @param params DjatokaEncodeParam containing compression parameters.
* @throws DjatokaException
*/
public void compressImage(BufferedImage bi, OutputStream output,
DjatokaEncodeParam params) throws DjatokaException {
if (params == null)
params = new DjatokaEncodeParam();
if (params.getLevels() == 0)
params.setLevels(ImageProcessingUtils.getLevelCount(bi.getWidth(), bi.getHeight()));
File in = null;
File out = null;
try {
in = IOUtils.createTempTiff(bi);
out = File.createTempFile("tmp", ".jp2");
compressImage(in.getAbsolutePath(), out.getAbsolutePath(), params);
IOUtils.copyStream(new FileInputStream(out), output);
} catch (IOException e) {
logger.error(e,e);
throw new DjatokaException(e);
} catch (Exception e) {
logger.error(e,e);
throw new DjatokaException(e);
}
if (in != null)
in.delete();
if (out != null)
out.delete();
}
/**
* Compress input using provided DjatokaEncodeParam parameters.
* @param input InputStream containing TIFF image bitstream
* @param output absolute file path for output file.
* @param params DjatokaEncodeParam containing compression parameters.
* @throws DjatokaException
*/
public void compressImage(InputStream input, String output,
DjatokaEncodeParam params) throws DjatokaException {
if (params == null)
params = new DjatokaEncodeParam();
File inputFile;
try {
inputFile = File.createTempFile("tmp", ".tif");
inputFile.deleteOnExit();
IOUtils.copyStream(input, new FileOutputStream(inputFile));
if (params.getLevels() == 0) {
ImageRecord dim = ImageRecordUtils.getImageDimensions(inputFile.getAbsolutePath());
params.setLevels(ImageProcessingUtils.getLevelCount(dim.getWidth(), dim.getHeight()));
dim = null;
}
} catch (IOException e) {
logger.error(e,e);
throw new DjatokaException(e);
}
compressImage(inputFile.getAbsolutePath(), output, params);
if (inputFile != null)
inputFile.delete();
}
/**
* Compress input using provided DjatokaEncodeParam parameters.
* @param input InputStream containing TIFF image bitstream
* @param output OutputStream to serialize compressed image.
* @param params DjatokaEncodeParam containing compression parameters.
* @throws DjatokaException
*/
public void compressImage(InputStream input, OutputStream output,
DjatokaEncodeParam params) throws DjatokaException {
if (params == null)
params = new DjatokaEncodeParam();
File inputFile = null;
try {
inputFile = File.createTempFile("tmp", ".tif");
IOUtils.copyStream(input, new FileOutputStream(inputFile));
if (params.getLevels() == 0) {
ImageRecord dim = ImageRecordUtils.getImageDimensions(inputFile.getAbsolutePath());
params.setLevels(ImageProcessingUtils.getLevelCount(dim.getWidth(), dim.getHeight()));
dim = null;
}
} catch (IOException e1) {
logger.error("Unexpected file format; expecting uncompressed TIFF",e1);
throw new DjatokaException("Unexpected file format; expecting uncompressed TIFF");
}
String out = STDOUT;
File winOut = null;
if (isWindows) {
try {
winOut = File.createTempFile("pipe_", ".jp2");
} catch (IOException e) {
logger.error(e,e);
throw new DjatokaException(e);
}
out = winOut.getAbsolutePath();
}
String command = getKduCompressCommand(inputFile.getAbsolutePath(),
out, params);
logger.debug("compressCommand: " + command);
Runtime rt = Runtime.getRuntime();
try {
final Process process = rt.exec(command, envParams, new File(env));
if (out.equals(STDOUT)) {
IOUtils.copyStream(process.getInputStream(), output);
} else if (isWindows) {
FileInputStream fis = new FileInputStream(out);
IOUtils.copyStream(fis, output);
fis.close();
}
process.waitFor();
if (process != null) {
String errorCheck = null;
try {
errorCheck = new String(IOUtils.getByteArray(process.getErrorStream()));
} catch (Exception e1) {
logger.error(e1,e1);
}
process.getInputStream().close();
process.getOutputStream().close();
process.getErrorStream().close();
process.destroy();
if (errorCheck != null)
throw new DjatokaException(errorCheck);
}
} catch (IOException e) {
logger.error(e,e);
throw new DjatokaException(e);
} catch (InterruptedException e) {
logger.error(e,e);
throw new DjatokaException(e);
}
if (inputFile != null)
inputFile.delete();
if (winOut != null)
winOut.delete();
}
/**
* Compress input using provided DjatokaEncodeParam parameters.
* @param input absolute file path for input file.
* @param output absolute file path for output file.
* @param params DjatokaEncodeParam containing compression parameters.
* @throws DjatokaException
*/
public void compressImage(String input, String output,
DjatokaEncodeParam params) throws DjatokaException {
if (params == null)
params = new DjatokaEncodeParam();
boolean tmp = false;
File inputFile = null;
if ((input.toLowerCase().endsWith(".tif")
|| input.toLowerCase().endsWith(".tiff")
|| ImageProcessingUtils.checkIfTiff(input))
&& ImageProcessingUtils.isUncompressedTiff(input)) {
logger.debug("Processing TIFF: " + input);
inputFile = new File(input);
} else {
try {
inputFile = IOUtils.createTempTiff(input);
tmp = true;
input = inputFile.getAbsolutePath();
} catch (Exception e) {
throw new DjatokaException("Unrecognized file format: " + e.getMessage());
}
}
if (params.getLevels() == 0) {
ImageRecord dim = ImageRecordUtils.getImageDimensions(inputFile.getAbsolutePath());
params.setLevels(ImageProcessingUtils.getLevelCount(dim.getWidth(), dim.getHeight()));
dim = null;
}
File outFile = new File(output);
String command = getKduCompressCommand(new File(input)
.getAbsolutePath(), outFile.getAbsolutePath(), params);
Runtime rt = Runtime.getRuntime();
try {
final Process process = rt.exec(command, envParams, new File(env));
process.waitFor();
if (process != null) {
String errorCheck = null;
try {
errorCheck = new String(IOUtils.getByteArray(process.getErrorStream()));
} catch (Exception e1) {
}
process.getInputStream().close();
process.getOutputStream().close();
process.getErrorStream().close();
process.destroy();
if (errorCheck != null && !errorCheck.equals(""))
throw new DjatokaException(errorCheck);
}
} catch (IOException e) {
logger.error(e,e);
throw new DjatokaException(e);
} catch (InterruptedException e) {
logger.error(e,e);
throw new DjatokaException(e);
} finally {
if (tmp)
inputFile.delete();
}
if (!outFile.getAbsolutePath().equals(STDOUT) && !outFile.exists())
throw new DjatokaException("Unknown error occurred during processing.");
}
/**
* Get kdu_compress command line for specified input, output, params.
* @param input absolute file path for input file.
* @param output absolute file path for output file.
* @param params DjatokaEncodeParam containing compression parameters.
* @return kdu_compress command line for specified input, output, params
*/
public static final String getKduCompressCommand(String input, String output,
DjatokaEncodeParam params) {
StringBuffer command = new StringBuffer(exe);
command.append(" -quiet -i ").append(escape(new File(input).getAbsolutePath()));
command.append(" -o ").append(escape(new File(output).getAbsolutePath()));
command.append(" ").append(toKduCompressArgs(params));
return command.toString();
}
private static final String escape(String path) {
if (path.contains(" "))
path = "\"" + path + "\"";
return path;
}
private static String toKduCompressArgs(DjatokaEncodeParam params) {
StringBuffer sb = new StringBuffer();
if (params.getRate() != null)
sb.append("-rate ").append(params.getRate()).append(" ");
else
sb.append("-slope ").append(params.getSlope()).append(" ");
if (params.getLevels() > 0)
sb.append("Clevels=").append(params.getLevels()).append(" ");
if (params.getPrecincts() != null)
sb.append("Cprecincts=").append(params.getPrecincts()).append(" ");
if (params.getLayers() > 0)
sb.append("Clayers=").append(params.getLayers()).append(" ");
if (params.getProgressionOrder() != null)
sb.append("Corder=").append(params.getProgressionOrder()).append(
" ");
if (params.getPacketDivision() != null)
sb.append("ORGtparts=").append(params.getPacketDivision()).append(
" ");
if (params.getCodeBlockSize() != null)
sb.append("Cblk=").append(params.getCodeBlockSize()).append(" ");
sb.append("ORGgen_plt=").append((params.getInsertPLT()) ? "yes" : "no")
.append(" ");
sb.append("Creversible=").append(
(params.getUseReversible()) ? "yes" : "no").append(" ");
return sb.toString();
}
}