/* * Copyright 1998-2014 University Corporation for Atmospheric Research/Unidata * * Portions of this software were developed by the Unidata Program at the * University Corporation for Atmospheric Research. * * Access and use of this software shall impose the following obligations * and understandings on the user. The user is granted the right, without * any fee or cost, to use, copy, modify, alter, enhance and distribute * this software, and any derivative works thereof, and its supporting * documentation for any purpose whatsoever, provided that this entire * notice appears in all copies of the software, derivative works and * supporting documentation. Further, UCAR requests that the user credit * UCAR/Unidata in any publications that result from the use of this * software or in any product that includes this software. The names UCAR * and/or Unidata, however, may not be used in any advertising or publicity * to endorse or promote any products or commercial entity unless specific * written permission is obtained from UCAR/Unidata. The user also * understands that UCAR/Unidata is not obligated to provide the user with * any support, consulting, training or assistance of any kind with regard * to the use, operation and performance of this software nor to provide * the user with any updates, revisions, new versions or "bug fixes." * * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "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 UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE. */ package ucar.nc2.writer; import ucar.jpeg.jj2000.j2k.codestream.writer.CodestreamWriter; import ucar.jpeg.jj2000.j2k.codestream.writer.FileCodestreamWriter; import ucar.jpeg.jj2000.j2k.codestream.writer.HeaderEncoder; import ucar.jpeg.jj2000.j2k.encoder.Encoder; import ucar.jpeg.jj2000.j2k.encoder.EncoderSpecs; import ucar.jpeg.jj2000.j2k.entropy.encoder.EntropyCoder; import ucar.jpeg.jj2000.j2k.entropy.encoder.PostCompRateAllocator; import ucar.jpeg.jj2000.j2k.fileformat.writer.FileFormatWriter; import ucar.jpeg.jj2000.j2k.image.BlkImgDataSrc; import ucar.jpeg.jj2000.j2k.image.ImgDataConverter; import ucar.jpeg.jj2000.j2k.image.ImgDataJoiner; import ucar.jpeg.jj2000.j2k.image.Tiler; import ucar.jpeg.jj2000.j2k.image.forwcomptransf.ForwCompTransf; import ucar.jpeg.jj2000.j2k.image.input.ImgReader; import ucar.jpeg.jj2000.j2k.image.input.ImgReaderPGM; import ucar.jpeg.jj2000.j2k.image.input.ImgReaderPGX; import ucar.jpeg.jj2000.j2k.image.input.ImgReaderPPM; import ucar.jpeg.jj2000.j2k.quantization.quantizer.Quantizer; import ucar.jpeg.jj2000.j2k.roi.encoder.ROIScaler; import ucar.jpeg.jj2000.j2k.util.CodestreamManipulator; import ucar.jpeg.jj2000.j2k.util.FacilityManager; import ucar.jpeg.jj2000.j2k.util.MsgLogger; import ucar.jpeg.jj2000.j2k.util.ParameterList; import ucar.jpeg.jj2000.j2k.wavelet.analysis.ForwardWT; import java.io.IOException; import java.io.StreamTokenizer; import java.io.StringReader; import java.util.NoSuchElementException; import java.util.StringTokenizer; import java.util.Vector; /** * Describe * * @author caron * @since 8/29/2014 */ public class Grib2JpegEncoder { /** * The exit code of the run method */ private int exitCode; /** * The parameter list (arguments) */ //private ParameterList pl; /** * The default parameter list (arguments) */ //private ParameterList defpl; /** * Returns the exit code of the class. This is only initialized after the * constructor and when the run method returns. * * @return The exit code of the constructor and the run() method. */ public int getExitCode() { return exitCode; } private ParameterList getParameterList(int nbits) { // not sure if these are needed in the bowels of jj2000 String[] argv = new String[] { "-rate", Integer.toString(nbits), "-verbose", "off", "-file_format", "off", // "-lossless", "on", // "Cannot use '-rate' and '-lossless' option at the same time. }; // Initialize default parameters ParameterList defpl = new ParameterList(); String[][] param = Encoder.getAllParameters(); for (int i = param.length - 1; i >= 0; i--) { if (param[i][3] != null) { defpl.put(param[i][0], param[i][3]); } } // Create parameter list using defaults ParameterList pl = new ParameterList(defpl); // Parse arguments from argv pl.parseArgs(argv); return pl; } int nbits; boolean debug; public Grib2JpegEncoder(int nbits, boolean debug) { this.nbits = nbits; this.debug = debug; } /** * Runs the encoder. After completion the exit code is set, a non-zero * value indicates that an error ocurred. * * @see #getExitCode */ public void run() { boolean verbose; boolean useFileFormat = false; boolean pphTile = false; boolean pphMain = false; boolean tempSop = false; boolean tempEph = false; ImgReader imreader[]; String inext, infile; StreamTokenizer stok; StringTokenizer sgtok; int ncomp; boolean ppminput; Vector imreadervec; boolean imsigned[]; BlkImgDataSrc imgsrc; int i; int imgcmpidxs[]; int tw, th; int refx, refy; int trefx, trefy; int pktspertp = 0; // LOOK Tiler imgtiler; BlkImgDataSrc cursrc; ForwCompTransf fctransf; ImgDataConverter converter; EncoderSpecs encSpec; ForwardWT dwt; Quantizer quant; ROIScaler rois; EntropyCoder ecoder; PostCompRateAllocator ralloc; HeaderEncoder headenc; CodestreamWriter bwriter; FileFormatWriter ffw; // String outname; int fileLength; ParameterList pl = getParameterList(nbits); float rate = nbits; // LOOK ?? try { // **** ImgReader **** sgtok = new StringTokenizer(pl.getParameter("i"), ","); ncomp = 0; ppminput = false; imreadervec = new Vector(); int nTokens = sgtok.countTokens(); for (int n = 0; n < nTokens; n++) { infile = sgtok.nextToken(); try { if (imreadervec.size() < ncomp) { error("With PPM input format only 1 input file can be specified", 2); return; } if (infile.lastIndexOf('.') != -1) { inext = infile.substring(infile.lastIndexOf('.'), infile.length()); } else { inext = null; } if (".PGM".equalsIgnoreCase(inext)) { // PGM file imreadervec.addElement(new ImgReaderPGM(infile)); ncomp += 1; } else if (".PPM".equalsIgnoreCase(inext)) { // PPM file if (ncomp > 0) { error("With PPM input format only 1 input " + "file can be specified", 2); return; } imreadervec.addElement(new ImgReaderPPM(infile)); ppminput = true; ncomp += 3; } else { // Should be PGX imreadervec.addElement(new ImgReaderPGX(infile)); ncomp += 1; } } catch (IOException e) { error("Could not open or read from file " + infile + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""), 3); if (pl.getParameter("debug").equals("on")) { e.printStackTrace(); } else { error("Use '-debug' option for more details", 2); } return; } finally { if (exitCode != 0) { // Close the other files while (imreadervec.size() > 0) { try { ((ImgReader) imreadervec.elementAt(imreadervec.size() - 1)).close(); imreadervec.removeElementAt(imreadervec.size() - 1); } catch (Exception e) { } } } } } imreader = new ImgReader[imreadervec.size()]; imreadervec.copyInto(imreader); imreadervec.removeAllElements(); imsigned = new boolean[ncomp]; // **** ImgDataJoiner (if needed) **** if (ppminput || ncomp == 1) { // Just one input imgsrc = imreader[0]; for (i = 0; i < ncomp; i++) { imsigned[i] = imreader[0].isOrigSigned(i); } } else { // More than one reader => join all readers into 1 imgcmpidxs = new int[ncomp]; for (i = 0; i < ncomp; i++) { imsigned[i] = imreader[i].isOrigSigned(0); } imgsrc = new ImgDataJoiner(imreader, imgcmpidxs); } // **** Tiler **** // get nominal tile dimensions stok = new StreamTokenizer(new StringReader(pl.getParameter("tiles"))); stok.eolIsSignificant(false); stok.nextToken(); if (stok.ttype != StreamTokenizer.TT_NUMBER) { error("An error occurred while parsing the tiles option: " + pl.getParameter("tiles"), 2); return; } tw = (int) stok.nval; stok.nextToken(); if (stok.ttype != StreamTokenizer.TT_NUMBER) { error("An error occurred while parsing the tiles option: " + pl.getParameter("tiles"), 2); return; } th = (int) stok.nval; // Get image reference point sgtok = new StringTokenizer(pl.getParameter("ref")); try { refx = Integer.parseInt(sgtok.nextToken()); refy = Integer.parseInt(sgtok.nextToken()); } catch (NoSuchElementException e) { throw new IllegalArgumentException("Error while parsing 'ref' option"); } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid number type in 'ref' option"); } if (refx < 0 || refy < 0) { throw new IllegalArgumentException("Invalid value in 'ref' option "); } // Get tiling reference point sgtok = new StringTokenizer(pl.getParameter("tref")); try { trefx = Integer.parseInt(sgtok.nextToken()); trefy = Integer.parseInt(sgtok.nextToken()); } catch (NoSuchElementException e) { throw new IllegalArgumentException("Error while parsing 'tref' option"); } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid number type in " + "'tref' option"); } if (trefx < 0 || trefy < 0 || trefx > refx || trefy > refy) { throw new IllegalArgumentException("Invalid value in 'tref' " + "option "); } // Instantiate tiler try { imgtiler = new Tiler(imgsrc, refx, refy, trefx, trefy, tw, th); } catch (IllegalArgumentException e) { error("Could not tile image" + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""), 2); if (pl.getParameter("debug").equals("on")) { e.printStackTrace(); } else { error("Use '-debug' option for more details", 2); } return; } int ntiles = imgtiler.getNumTiles(); // **** Encoder specifications **** encSpec = new EncoderSpecs(ntiles, ncomp, imgsrc, pl); // **** Component transformation **** if (ppminput && pl.getParameter("Mct") != null && pl.getParameter("Mct").equals("off")) { FacilityManager.getMsgLogger(). printmsg(MsgLogger.WARNING, "Input image is RGB and no color transform has " + "been specified. Compression performance and " + "image quality might be greatly degraded. Use " + "the 'Mct' option to specify a color transform"); } try { fctransf = new ForwCompTransf(imgtiler, encSpec); } catch (IllegalArgumentException e) { error("Could not instantiate forward component transformation" + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""), 2); if (pl.getParameter("debug").equals("on")) { e.printStackTrace(); } else { error("Use '-debug' option for more details", 2); } return; } // **** ImgDataConverter **** converter = new ImgDataConverter(fctransf); // **** ForwardWT **** try { dwt = ForwardWT.createInstance(converter, pl, encSpec); } catch (IllegalArgumentException e) { error("Could not instantiate wavelet transform" + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""), 2); if (pl.getParameter("debug").equals("on")) { e.printStackTrace(); } else { error("Use '-debug' option for more details", 2); } return; } // **** Quantizer **** try { quant = Quantizer.createInstance(dwt, encSpec); } catch (IllegalArgumentException e) { error("Could not instantiate quantizer" + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""), 2); if (pl.getParameter("debug").equals("on")) { e.printStackTrace(); } else { error("Use '-debug' option for more details", 2); } return; } // **** ROIScaler **** try { rois = ROIScaler.createInstance(quant, pl, encSpec); } catch (IllegalArgumentException e) { error("Could not instantiate ROI scaler" + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""), 2); if (pl.getParameter("debug").equals("on")) { e.printStackTrace(); } else { error("Use '-debug' option for more details", 2); } return; } // **** EntropyCoder **** try { ecoder = EntropyCoder.createInstance(rois, pl, encSpec.cblks, encSpec.pss, encSpec.bms, encSpec.mqrs, encSpec.rts, encSpec.css, encSpec.sss, encSpec.lcs, encSpec.tts); } catch (IllegalArgumentException e) { error("Could not instantiate entropy coder" + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""), 2); if (pl.getParameter("debug").equals("on")) { e.printStackTrace(); } else { error("Use '-debug' option for more details", 2); } return; } String outname = null; // **** CodestreamWriter **** try { // Rely on rate allocator to limit amount of data bwriter = new FileCodestreamWriter(outname, Integer.MAX_VALUE); } catch (IOException e) { error("Could not open output file" + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""), 2); if (pl.getParameter("debug").equals("on")) { e.printStackTrace(); } else { error("Use '-debug' option for more details", 2); } return; } // **** Rate allocator **** try { ralloc = PostCompRateAllocator.createInstance(ecoder, pl, rate, bwriter, encSpec); } catch (IllegalArgumentException e) { error("Could not instantiate rate allocator" + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""), 2); if (pl.getParameter("debug").equals("on")) { e.printStackTrace(); } else { error("Use '-debug' option for more details", 2); } return; } // **** HeaderEncoder **** headenc = new HeaderEncoder(imgsrc, imsigned, dwt, imgtiler, encSpec, rois, ralloc, pl); ralloc.setHeaderEncoder(headenc); // **** Write header to be able to estimate header overhead **** headenc.encodeMainHeader(); // **** Initialize rate allocator, with proper header // overhead. This will also encode all the data **** ralloc.initialize(); // **** Write header (final) **** headenc.reset(); headenc.encodeMainHeader(); // Insert header into the codestream bwriter.commitBitstreamHeader(headenc); // **** Now do the rate-allocation and write result **** ralloc.runAndWrite(); // **** Done **** bwriter.close(); // **** Calculate file length **** fileLength = bwriter.getLength(); // **** Tile-parts and packed packet headers **** if (pktspertp > 0 || pphTile || pphMain) { int headInc; try { CodestreamManipulator cm = new CodestreamManipulator(outname, ntiles, pktspertp, pphMain, pphTile, tempSop, tempEph); fileLength += cm.doCodestreamManipulation(); String res = ""; if (pktspertp > 0) { FacilityManager. getMsgLogger().println("Created tile-parts " + "containing at most " + pktspertp + " packets per tile.", 4, 6); } if (pphTile) { FacilityManager.getMsgLogger(). println("Moved packet headers " + "to tile headers", 4, 6); } if (pphMain) { FacilityManager.getMsgLogger(). println("Moved packet headers " + "to main header", 4, 6); } } catch (IOException e) { error("Error while creating tileparts or packed packet" + " headers" + ((e.getMessage() != null) ? (":\n" + e.getMessage()) : ""), 2); if (pl.getParameter("debug").equals("on")) { e.printStackTrace(); } else { error("Use '-debug' option for more details", 2); } return; } } // **** File Format **** if (useFileFormat) { try { int nc = imgsrc.getNumComps(); int[] bpc = new int[nc]; for (int comp = 0; comp < nc; comp++) { bpc[comp] = imgsrc.getNomRangeBits(comp); } ffw = new FileFormatWriter(outname, imgsrc.getImgHeight(), imgsrc.getImgWidth(), nc, bpc, fileLength); fileLength += ffw.writeFileFormat(); } catch (IOException e) { throw new Error("Error while writing JP2 file format"); } } // **** Close image reader(s) *** for (i = 0; i < imreader.length; i++) { imreader[i].close(); } } catch (Throwable e) { error("An unchecked exception has occurred: " + e.getMessage(), 2); if (debug) { e.printStackTrace(); } } } /** * Prints the error message 'msg' to standard err, prepending "ERROR" to * it, and sets the exitCode to 'code'. An exit code different than 0 * indicates that there where problems. * * @param msg The error message * @param code The exit code to set */ private void error(String msg, int code) { exitCode = code; FacilityManager.getMsgLogger().printmsg(MsgLogger.ERROR, msg); } /** * Prints the warning message 'msg' to standard err, prepending "WARNING" * to it. * * @param msg The error message */ private void warning(String msg) { FacilityManager.getMsgLogger().printmsg(MsgLogger.WARNING, msg); } }