/*
* #%L
* Fork of JAI Image I/O Tools.
* %%
* Copyright (C) 2008 - 2015 Open Microscopy Environment:
* - Board of Regents of the University of Wisconsin-Madison
* - Glencoe Software, Inc.
* - University of Dundee
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
/*
* $RCSfile: EBCOTRateAllocator.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:08 $
* $State: Exp $
*
* Class: EBCOTRateAllocator
*
* Description: Generic interface for post-compression
* rate allocator.
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
* */
package jj2000.j2k.entropy.encoder;
import java.awt.Point;
import jj2000.j2k.codestream.writer.*;
import jj2000.j2k.wavelet.analysis.*;
import jj2000.j2k.entropy.encoder.*;
import jj2000.j2k.codestream.*;
import jj2000.j2k.entropy.*;
import jj2000.j2k.image.*;
import jj2000.j2k.util.*;
import java.util.Vector;
import java.io.*;
import com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageWriteParamJava;
/**
* This implements the EBCOT post compression rate allocation algorithm. This
* algorithm finds the most suitable truncation points for the set of
* code-blocks, for each layer target bitrate. It works by first collecting
* the rate distortion info from all code-blocks, in all tiles and all
* components, and then running the rate-allocation on the whole image at
* once, for each layer.
*
* <P>This implementation also provides some timing features. They can be
* enabled by setting the 'DO_TIMING' constant of this class to true and
* recompiling. The timing uses the 'System.currentTimeMillis()' Java API
* call, which returns wall clock time, not the actual CPU time used. The
* timing results will be printed on the message output. Since the times
* reported are wall clock times and not CPU usage times they can not be added
* to find the total used time (i.e. some time might be counted in several
* places). When timing is disabled ('DO_TIMING' is false) there is no penalty
* if the compiler performs some basic optimizations. Even if not the penalty
* should be negligeable.
*
* @see PostCompRateAllocator
*
* @see CodedCBlkDataSrcEnc
*
* @see jj2000.j2k.codestream.writer.CodestreamWriter
* */
public class EBCOTRateAllocator extends PostCompRateAllocator {
/** Whether to collect timing information or not: false. Used as a compile
* time directive. */
private final static boolean DO_TIMING = false;
/** The wall time for the initialization. */
private long initTime;
/** The wall time for the building of layers. */
private long buildTime;
/** The wall time for the writing of layers. */
private long writeTime;
/**
* 5D Array containing all the coded code-blocks:
*
* <ul>
* <li>1st index: tile index</li>
* <li>2nd index: component index</li>
* <li>3rd index: resolution level index</li>
* <li>4th index: subband index</li>
* <li>5th index: code-block index</li>
* </ul>
**/
private CBlkRateDistStats cblks[][][][][];
/**
* 6D Array containing the indices of the truncation points. It actually
* contains the index of the element in CBlkRateDistStats.truncIdxs that
* gives the real truncation point index.
*
* <ul>
* <li>1st index: tile index</li>
* <li>2nd index: layer index</li>
* <li>3rd index: component index</li>
* <li>4th index: resolution level index</li>
* <li>5th index: subband index</li>
* <li>6th index: code-block index</li>
* </ul>
**/
private int truncIdxs[][][][][][];
/**
* Maximum number of precincts :
*
* <ul>
* <li>1st dim: tile index.</li>
* <li>2nd dim: component index.</li>
* <li>3nd dim: resolution level index.</li>
* </ul>
*/
private Point numPrec[][][];
/** Array containing the layers information. */
private EBCOTLayer layers[];
/** The log of 2, natural base */
private static final double LOG2 = Math.log(2);
/** The normalization offset for the R-D summary table */
private static final int RD_SUMMARY_OFF = 24;
/** The size of the summary table */
private static final int RD_SUMMARY_SIZE = 64;
/** The relative precision for float data. This is the relative tolerance
* up to which the layer slope thresholds are calculated. */
private static final float FLOAT_REL_PRECISION = 1e-4f;
/** The precision for float data type, in an absolute sense. Two float
* numbers are considered "equal" if they are within this precision. */
private static final float FLOAT_ABS_PRECISION = 1e-10f;
/**
* Minimum average size of a packet. If layer has less bytes than the this
* constant multiplied by number of packets in the layer, then the layer
* is skipped.
* */
private static final int MIN_AVG_PACKET_SZ = 32;
/**
* The R-D summary information collected from the coding of all
* code-blocks. For each entry it contains the accumulated length of all
* truncation points that have a slope not less than
* '2*(k-RD_SUMMARY_OFF)', where 'k' is the entry index.
*
* <P>Therefore, the length at entry 'k' is the total number of bytes of
* code-block data that would be obtained if the truncation slope was
* chosen as '2*(k-RD_SUMMARY_OFF)', without counting the overhead
* associated with the packet heads.
*
* <P>This summary is used to estimate the relation of the R-D slope to
* coded length, and to obtain absolute minimums on the slope given a
* length.
**/
private int RDSlopesRates[];
/** Packet encoder. */
private PktEncoder pktEnc;
/** The layer specifications */
private LayersInfo lyrSpec;
/** The maximum slope accross all code-blocks and truncation points. */
private float maxSlope;
/** The minimum slope accross all code-blocks and truncation points. */
private float minSlope;
/**
* Initializes the EBCOT rate allocator of entropy coded data. The layout
* of layers, and their bitrate constraints, is specified by the 'lyrs'
* parameter.
*
* @param src The source of entropy coded data.
*
* @param lyrs The layers layout specification.
*
* @param writer The bit stream writer.
*
* @see ProgressionType
* */
public EBCOTRateAllocator(CodedCBlkDataSrcEnc src, LayersInfo lyrs,
CodestreamWriter writer,
J2KImageWriteParamJava wp) {
super(src,lyrs.getTotNumLayers(),writer,wp);
int minsbi, maxsbi;
int i;
SubbandAn sb, sb2;
Point ncblks = null;
// If we do timing create necessary structures
if (DO_TIMING) {
// If we are timing make sure that 'finalize' gets called.
//System.runFinalizersOnExit(true);
// The System.runFinalizersOnExit() method is deprecated in Java
// 1.2 since it can cause a deadlock in some cases. However, here
// we use it only for profiling purposes and is disabled in
// production code.
initTime = 0L;
buildTime = 0L;
writeTime = 0L;
}
// Save the layer specs
lyrSpec = lyrs;
//Initialize the size of the RD slope rates array
RDSlopesRates = new int[RD_SUMMARY_SIZE];
//Get number of tiles, components
int nt = src.getNumTiles();
int nc = getNumComps();
//Allocate the coded code-blocks and truncation points indexes arrays
cblks = new CBlkRateDistStats[nt][nc][][][];
truncIdxs = new int[nt][numLayers][nc][][][];
int cblkPerSubband; // Number of code-blocks per subband
int mrl; // Number of resolution levels
int l; // layer index
int s; //subband index
// Used to compute the maximum number of precincts for each resolution
// level
int tx0, ty0, tx1, ty1; // Current tile position in the reference grid
int tcx0, tcy0, tcx1, tcy1; // Current tile position in the domain of
// the image component
int trx0, try0, trx1, try1; // Current tile position in the reduced
// resolution image domain
int xrsiz, yrsiz; // Component sub-sampling factors
Point tileI = null;
Point nTiles = null;
int xsiz,ysiz,x0siz,y0siz;
int xt0siz,yt0siz;
int xtsiz,ytsiz;
int cb0x = src.getCbULX();
int cb0y = src.getCbULY();
src.setTile(0,0);
for (int t=0; t<nt; t++) { // Loop on tiles
nTiles = src.getNumTiles(nTiles);
tileI = src.getTile(tileI);
x0siz = getImgULX();
y0siz = getImgULY();
xsiz = x0siz + getImgWidth();
ysiz = y0siz + getImgHeight();
xt0siz = src.getTilePartULX();
yt0siz = src.getTilePartULY();
xtsiz = src.getNomTileWidth();
ytsiz = src.getNomTileHeight();
// Tile's coordinates on the reference grid
tx0 = (tileI.x==0) ? x0siz : xt0siz+tileI.x*xtsiz;
ty0 = (tileI.y==0) ? y0siz : yt0siz+tileI.y*ytsiz;
tx1 = (tileI.x!=nTiles.x-1) ? xt0siz+(tileI.x+1)*xtsiz : xsiz;
ty1 = (tileI.y!=nTiles.y-1) ? yt0siz+(tileI.y+1)*ytsiz : ysiz;
for(int c=0; c<nc; c++) { // loop on components
//Get the number of resolution levels
sb = src.getAnSubbandTree(t,c);
mrl = sb.resLvl+1;
// Initialize maximum number of precincts per resolution array
if (numPrec==null) { numPrec = new Point[nt][nc][]; }
if (numPrec[t][c]==null) {
numPrec[t][c] = new Point[mrl];
}
// Subsampling factors
xrsiz = src.getCompSubsX(c);
yrsiz = src.getCompSubsY(c);
// Tile's coordinates in the image component domain
tcx0 = (int)Math.ceil(tx0/(double)(xrsiz));
tcy0 = (int)Math.ceil(ty0/(double)(yrsiz));
tcx1 = (int)Math.ceil(tx1/(double)(xrsiz));
tcy1 = (int)Math.ceil(ty1/(double)(yrsiz));
cblks[t][c] = new CBlkRateDistStats[mrl][][];
for(l=0; l<numLayers; l++) {
truncIdxs[t][l][c] = new int[mrl][][];
}
for(int r=0; r<mrl; r++) { // loop on resolution levels
// Tile's coordinates in the reduced resolution image
// domain
trx0 = (int)Math.ceil(tcx0/(double)(1<<(mrl-1-r)));
try0 = (int)Math.ceil(tcy0/(double)(1<<(mrl-1-r)));
trx1 = (int)Math.ceil(tcx1/(double)(1<<(mrl-1-r)));
try1 = (int)Math.ceil(tcy1/(double)(1<<(mrl-1-r)));
// Calculate the maximum number of precincts for each
// resolution level taking into account tile specific
// options.
double twoppx = (double)wp.getPrecinctPartition().getPPX(t,c,r);
double twoppy = (double)wp.getPrecinctPartition().getPPY(t,c,r);
numPrec[t][c][r] = new Point();
if (trx1>trx0) {
numPrec[t][c][r].x = (int)Math.ceil((trx1-cb0x)/twoppx)
- (int)Math.floor((trx0-cb0x)/twoppx);
} else {
numPrec[t][c][r].x = 0;
}
if (try1>try0) {
numPrec[t][c][r].y = (int)Math.ceil((try1-cb0y)/twoppy)
- (int)Math.floor((try0-cb0y)/(double)twoppy);
} else {
numPrec[t][c][r].y = 0;
}
minsbi = (r==0) ? 0 : 1;
maxsbi = (r==0) ? 1 : 4;
cblks[t][c][r] = new CBlkRateDistStats[maxsbi][];
for(l=0; l<numLayers; l++) {
truncIdxs[t][l][c][r] = new int[maxsbi][];
}
for(s=minsbi; s<maxsbi; s++) { // loop on subbands
//Get the number of blocks in the current subband
sb2 = (SubbandAn)sb.getSubbandByIdx(r,s);
ncblks = sb2.numCb;
cblkPerSubband = ncblks.x*ncblks.y;
cblks[t][c][r][s] =
new CBlkRateDistStats[cblkPerSubband];
for(l=0; l<numLayers; l++) {
truncIdxs[t][l][c][r][s] = new int[cblkPerSubband];
for(i=0; i<cblkPerSubband; i++) {
truncIdxs[t][l][c][r][s][i] = -1;
}
}
} // End loop on subbands
} // End lopp on resolution levels
} // End loop on components
if (t!=nt-1) {
src.nextTile();
}
} // End loop on tiles
//Initialize the packet encoder
pktEnc = new PktEncoder(src,wp,numPrec);
// The layers array has to be initialized after the constructor since
// it is needed that the bit stream header has been entirely written
}
/**
* Prints the timing information, if collected, and calls 'finalize' on
* the super class.
* */
public void finalize() throws Throwable {
if (DO_TIMING) {
StringBuffer sb;
sb = new StringBuffer("EBCOTRateAllocator wall clock times:\n");
sb.append(" initialization: ");
sb.append(initTime);
sb.append(" ms\n");
sb.append(" layer building: ");
sb.append(buildTime);
sb.append(" ms\n");
sb.append(" final writing: ");
sb.append(writeTime);
sb.append(" ms");
FacilityManager.getMsgLogger().
printmsg(MsgLogger.INFO,sb.toString());
}
super.finalize();
}
/**
* Runs the rate allocation algorithm and writes the data to the bit
* stream writer object provided to the constructor.
* */
public void runAndWrite() throws IOException {
//Now, run the rate allocation
buildAndWriteLayers();
}
/**
* Initializes the layers array. This must be called after the main header
* has been entirely written or simulated, so as to take its overhead into
* account. This method will get all the code-blocks and then initialize
* the target bitrates for each layer, according to the specifications.
* */
public void initialize() throws IOException{
int n,i,l;
int ho; // The header overhead (in bytes)
float np;// The number of pixels divided by the number of bits per byte
double ls; // Step for log-scale
double basebytes;
int lastbytes,newbytes,nextbytes;
int loopnlyrs;
int minlsz; // The minimum allowable number of bytes in a layer
int totenclength;
int maxpkt;
int numTiles = src.getNumTiles();
int numComps = src.getNumComps();
int numLvls;
int avgPktLen;
long stime = 0L;
// Start by getting all the code-blocks, we need this in order to have
// an idea of the total encoded bitrate.
getAllCodeBlocks();
if (DO_TIMING) stime = System.currentTimeMillis();
// Now get the total encoded length
totenclength = RDSlopesRates[0]; // all the encoded data
// Make a rough estimation of the packet head overhead, as 2 bytes per
// packet in average (plus EPH / SOP) , and add that to the total
// encoded length
for( int t=0 ; t<numTiles ; t++ ){
avgPktLen = 2;
// Add SOP length if set
if (((String)wp.getSOP().getTileDef(t)).equalsIgnoreCase("true")) {
avgPktLen += Markers.SOP_LENGTH;
}
// Add EPH length if set
if (((String)wp.getEPH().getTileDef(t)).equalsIgnoreCase("true")) {
avgPktLen += Markers.EPH_LENGTH;
}
for( int c=0 ; c<numComps ; c++ ){
numLvls = src.getAnSubbandTree(t,c).resLvl+1;
if( !src.precinctPartitionUsed(c,t) ) {
// Precinct partition is not used so there is only
// one packet per resolution level/layer
totenclength += numLayers*avgPktLen*numLvls;
}
else {
// Precinct partition is used so for each
// component/tile/resolution level, we get the maximum
// number of packets
for ( int rl=0 ; rl<numLvls ; rl++ ) {
maxpkt = numPrec[t][c][rl].x * numPrec[t][c][rl].y;
totenclength += numLayers*avgPktLen*maxpkt;
}
}
} // End loop on components
} // End loop on tiles
// If any layer specifies more than 'totenclength' as its target
// length then 'totenclength' is used. This is to prevent that
// estimated layers get excessively large target lengths due to an
// excessively large target bitrate. At the end the last layer is set
// to the target length corresponding to the overall target
// bitrate. Thus, 'totenclength' can not limit the total amount of
// encoded data, as intended.
ho = headEnc.getLength();
np = src.getImgWidth()*src.getImgHeight()/8f;
// SOT marker must be taken into account
for(int t=0; t<numTiles; t++){
headEnc.reset();
headEnc.encodeTilePartHeader(0,t);
ho += headEnc.getLength();
}
layers = new EBCOTLayer[numLayers];
for (n = numLayers-1; n>=0; n--) {
layers[n] = new EBCOTLayer();
}
minlsz = 0; // To keep compiler happy
for( int t=0 ; t<numTiles ; t++ ){
for( int c=0 ; c<numComps ; c++ ){
numLvls = src.getAnSubbandTree(t,c).resLvl+1;
if ( !src.precinctPartitionUsed(c,t) ) {
// Precinct partition is not used
minlsz += MIN_AVG_PACKET_SZ*numLvls;
}
else {
// Precinct partition is used
for ( int rl=0 ; rl<numLvls ; rl++ ) {
maxpkt = numPrec[t][c][rl].x * numPrec[t][c][rl].y;
minlsz += MIN_AVG_PACKET_SZ*maxpkt;
}
}
} // End loop on components
} // End loop on tiles
// Initialize layers
n = 0;
i = 0;
lastbytes = 0;
while (n < numLayers-1) {
// At an optimized layer
basebytes = Math.floor(lyrSpec.getTargetBitrate(i)*np);
if (i < lyrSpec.getNOptPoints()-1) {
nextbytes = (int) (lyrSpec.getTargetBitrate(i+1)*np);
// Limit target length to 'totenclength'
if (nextbytes > totenclength)
nextbytes = totenclength;
}
else {
nextbytes = 1;
}
loopnlyrs = lyrSpec.getExtraLayers(i)+1;
ls = Math.exp(Math.log((double)nextbytes/basebytes)/loopnlyrs);
layers[n].optimize = true;
for (l = 0; l < loopnlyrs; l++) {
newbytes = (int)basebytes - lastbytes - ho;
if (newbytes < minlsz) { // Skip layer (too small)
basebytes *= ls;
numLayers--;
continue;
}
lastbytes = (int)basebytes - ho;
layers[n].maxBytes = lastbytes;
basebytes *= ls;
n++;
}
i++; // Goto next optimization point
}
// Ensure minimum size of last layer (this one determines overall
// bitrate)
n = numLayers-2;
nextbytes = (int) (lyrSpec.getTotBitrate()*np) - ho;
newbytes = nextbytes - ((n>=0) ? layers[n].maxBytes : 0);
while (newbytes < minlsz) {
if (numLayers == 1) {
if (newbytes <= 0) {
throw new
IllegalArgumentException("Overall target bitrate too "+
"low, given the current "+
"bit stream header overhead");
}
break;
}
// Delete last layer
numLayers--;
n--;
newbytes = nextbytes - ((n>=0) ? layers[n].maxBytes : 0);
}
// Set last layer to the overall target bitrate
n++;
layers[n].maxBytes = nextbytes;
layers[n].optimize = true;
// Re-initialize progression order changes if needed Default values
Progression[] prog1,prog2;
prog1 = (Progression[])wp.getProgressionType().getDefault();
int nValidProg = prog1.length;
for(int prg=0; prg<prog1.length;prg++){
if(prog1[prg].lye>numLayers){
prog1[prg].lye = numLayers;
}
}
if(nValidProg==0)
throw new Error("Unable to initialize rate allocator: No "+
"default progression type has been defined.");
// Tile specific values
for(int t=0; t<numTiles; t++){
if(wp.getProgressionType().isTileSpecified(t)){
prog1 = (Progression[])wp.getProgressionType().getTileDef(t);
nValidProg = prog1.length;
for(int prg=0; prg<prog1.length;prg++){
if(prog1[prg].lye>numLayers){
prog1[prg].lye = numLayers;
}
}
if(nValidProg==0)
throw new Error("Unable to initialize rate allocator: No "+
"default progression type has been defined.");
}
} // End loop on tiles
if (DO_TIMING) initTime += System.currentTimeMillis()-stime;
}
/**
* This method gets all the coded code-blocks from the EBCOT entropy coder
* for every component and every tile. Each coded code-block is stored in
* a 5D array according to the component, the resolution level, the tile,
* the subband it belongs and its position in the subband.
*
* <P> For each code-block, the valid slopes are computed and converted
* into the mantissa-exponent representation.
* */
private void getAllCodeBlocks() {
int numComps, numTiles, numBytes;
int c, r, t, s, sidx, k;
int slope;
SubbandAn subb;
CBlkRateDistStats ccb = null;
Point ncblks = null;
int last_sidx;
float fslope;
long stime = 0L;
maxSlope = 0f;
minSlope = Float.MAX_VALUE;
//Get the number of components and tiles
numComps = src.getNumComps();
numTiles = src.getNumTiles();
SubbandAn root,sb;
int cblkToEncode = 0;
int nEncCblk = 0;
ProgressWatch pw = FacilityManager.getProgressWatch();
//Get all coded code-blocks Goto first tile
src.setTile(0,0);
for (t=0; t<numTiles; t++) { //loop on tiles
nEncCblk = 0;
cblkToEncode = 0;
for(c=0; c<numComps; c++) {
root = src.getAnSubbandTree(t,c);
for(r=0; r<=root.resLvl; r++) {
if(r==0) {
sb = (SubbandAn)root.getSubbandByIdx(0,0);
if(sb!=null) cblkToEncode += sb.numCb.x*sb.numCb.y;
} else {
sb = (SubbandAn)root.getSubbandByIdx(r,1);
if(sb!=null) cblkToEncode += sb.numCb.x*sb.numCb.y;
sb = (SubbandAn)root.getSubbandByIdx(r,2);
if(sb!=null) cblkToEncode += sb.numCb.x*sb.numCb.y;
sb = (SubbandAn)root.getSubbandByIdx(r,3);
if(sb!=null) cblkToEncode += sb.numCb.x*sb.numCb.y;
}
}
}
if(pw!=null) {
pw.initProgressWatch(0,cblkToEncode,"Encoding tile "+t+"...");
}
for (c=0; c<numComps; c++) { //loop on components
//Get next coded code-block coordinates
while ( (ccb = src.getNextCodeBlock(c,ccb)) != null) {
if (DO_TIMING) stime = System.currentTimeMillis();
if(pw!=null) {
nEncCblk++;
pw.updateProgressWatch(nEncCblk,null);
}
subb = ccb.sb;
//Get the coded code-block resolution level index
r = subb.resLvl;
//Get the coded code-block subband index
s = subb.sbandIdx;
//Get the number of blocks in the current subband
ncblks = subb.numCb;
// Add code-block contribution to summary R-D table
// RDSlopesRates
last_sidx = -1;
for (k=ccb.nVldTrunc-1; k>=0; k--) {
fslope = ccb.truncSlopes[k];
if (fslope > maxSlope) maxSlope = fslope;
if (fslope < minSlope) minSlope = fslope;
sidx = getLimitedSIndexFromSlope(fslope);
for (; sidx > last_sidx; sidx--) {
RDSlopesRates[sidx] +=
ccb.truncRates[ccb.truncIdxs[k]];
}
last_sidx = getLimitedSIndexFromSlope(fslope);
}
//Fills code-blocks array
cblks[t][c][r][s][(ccb.m*ncblks.x)+ccb.n] = ccb;
ccb = null;
if(DO_TIMING) initTime += System.currentTimeMillis()-stime;
}
}
if(pw!=null) {
pw.terminateProgressWatch();
}
//Goto next tile
if(t<numTiles-1) //not at last tile
src.nextTile();
}
}
/**
* This method builds all the bit stream layers and then writes them to
* the output bit stream. Firstly it builds all the layers by computing
* the threshold according to the layer target bit-rate, and then it
* writes the layer bit streams according to the Progression type.
* */
private void buildAndWriteLayers() throws IOException {
int nPrec = 0;
int maxBytes, actualBytes;
float rdThreshold;
SubbandAn sb;
float threshold;
BitOutputBuffer hBuff = null;
byte[] bBuff = null;
int[] tileLengths; // Length of each tile
int tmp;
boolean sopUsed; // Should SOP markers be used ?
boolean ephUsed; // Should EPH markers be used ?
int nc = src.getNumComps();
int nt = src.getNumTiles();
int mrl;
long stime = 0L;
if (DO_TIMING) stime = System.currentTimeMillis();
// Start with the maximum slope
rdThreshold = maxSlope;
tileLengths = new int[nt];
actualBytes = 0;
// +------------------------------+
// | First we build the layers |
// +------------------------------+
// Bitstream is simulated to know tile length
for(int l=0; l<numLayers; l++){ //loop on layers
maxBytes = layers[l].maxBytes;
if(layers[l].optimize) {
rdThreshold =
optimizeBitstreamLayer(l,rdThreshold,maxBytes,actualBytes);
} else {
if( l<=0 || l>=numLayers-1 ) {
throw new IllegalArgumentException("The first and the"+
" last layer "+
"thresholds"+
" must be optimized");
}
rdThreshold = estimateLayerThreshold(maxBytes,layers[l-1]);
}
for(int t=0; t<nt; t++) { //loop on tiles
if(l==0) {
// Tile header
headEnc.reset();
headEnc.encodeTilePartHeader(0,t);
tileLengths[t] += headEnc.getLength();
}
for(int c=0; c<nc; c++) { //loop on components
// set boolean sopUsed here (SOP markers)
sopUsed = ((String)wp.getSOP().getTileDef(t)).
equalsIgnoreCase("true");
// set boolean ephUsed here (EPH markers)
ephUsed = ((String)wp.getEPH().getTileDef(t)).
equalsIgnoreCase("true");
// Go to LL band
sb = src.getAnSubbandTree(t,c);
mrl = sb.resLvl+1;
while (sb.subb_LL!=null) {
sb = sb.subb_LL;
}
for(int r=0; r<mrl ; r++) { // loop on resolution levels
nPrec = numPrec[t][c][r].x*numPrec[t][c][r].y;
for(int p=0; p<nPrec; p++) { // loop on precincts
findTruncIndices(l,c,r,t,sb,rdThreshold,p);
hBuff =
pktEnc.encodePacket(l+1,c,r,t,
cblks[t][c][r],
truncIdxs[t][l][c][r],
hBuff, bBuff,p);
if(pktEnc.isPacketWritable()) {
tmp = bsWriter.
writePacketHead(hBuff.getBuffer(),
hBuff.getLength(),
true, sopUsed,ephUsed);
tmp += bsWriter.
writePacketBody(pktEnc.getLastBodyBuf(),
pktEnc.getLastBodyLen(),
true,pktEnc.isROIinPkt(),
pktEnc.getROILen());
actualBytes += tmp;
tileLengths[t] += tmp;
}
} // End loop on precincts
sb = sb.parent;
} // End loop on resolution levels
} // End loop on components
} // end loop on tiles
layers[l].rdThreshold = rdThreshold;
layers[l].actualBytes = actualBytes;
} // end loop on layers
if (DO_TIMING) buildTime += System.currentTimeMillis()-stime;
// The bit-stream was not yet generated (only simulated).
if (DO_TIMING) stime = System.currentTimeMillis();
// +--------------------------------------------------+
// | Write tiles according to their Progression order |
// +--------------------------------------------------+
// Reset the packet encoder before writing all packets
pktEnc.reset();
Progression[] prog; // Progression(s) in each tile
int cs,ce,rs,re,lye;
int[] mrlc = new int[nc];
for(int t=0; t<nt; t++) { //loop on tiles
int[][] lysA; // layer index start for each component and
// resolution level
int[][] lys = new int[nc][];
for(int c=0; c<nc; c++){
mrlc[c] = src.getAnSubbandTree(t,c).resLvl;
lys[c] = new int[mrlc[c]+1];
}
// Tile header
headEnc.reset();
headEnc.encodeTilePartHeader(tileLengths[t],t);
bsWriter.commitBitstreamHeader(headEnc);
prog = (Progression[])wp.getProgressionType().getTileDef(t);
for(int prg=0; prg<prog.length;prg++){ // Loop on progression
lye = prog[prg].lye;
cs = prog[prg].cs;
ce = prog[prg].ce;
rs = prog[prg].rs;
re = prog[prg].re;
switch(prog[prg].type){
case ProgressionType.RES_LY_COMP_POS_PROG:
writeResLyCompPos(t,rs,re,cs,ce,lys,lye);
break;
case ProgressionType.LY_RES_COMP_POS_PROG:
writeLyResCompPos(t,rs,re,cs,ce,lys,lye);
break;
case ProgressionType.POS_COMP_RES_LY_PROG:
writePosCompResLy(t,rs,re,cs,ce,lys,lye);
break;
case ProgressionType.COMP_POS_RES_LY_PROG:
writeCompPosResLy(t,rs,re,cs,ce,lys,lye);
break;
case ProgressionType.RES_POS_COMP_LY_PROG:
writeResPosCompLy(t,rs,re,cs,ce,lys,lye);
break;
default:
throw new Error("Unsupported bit stream progression type");
} // switch on progression
// Update next first layer index
for(int c=cs; c<ce; c++)
for(int r=rs; r<re; r++){
if(r>mrlc[c]) continue;
lys[c][r] = lye;
}
} // End loop on progression
} // End loop on tiles
if (DO_TIMING) writeTime += System.currentTimeMillis()-stime;
}
/**
* Write a piece of bit stream according to the
* RES_LY_COMP_POS_PROG progression mode and between given bounds
*
* @param t Tile index.
*
* @param rs First resolution level index.
*
* @param re Last resolution level index.
*
* @param cs First component index.
*
* @param ce Last component index.
*
* @param lys First layer index for each component and resolution.
*
* @param lye Index of the last layer.
* */
public void writeResLyCompPos(int t,int rs,int re,int cs,int ce,
int lys[][],int lye) throws IOException {
boolean sopUsed; // Should SOP markers be used ?
boolean ephUsed; // Should EPH markers be used ?
int nc = src.getNumComps();
int[] mrl = new int[nc];
SubbandAn sb;
float threshold;
BitOutputBuffer hBuff = null;
byte[] bBuff = null;
int nPrec = 0;
// Max number of resolution levels in the tile
int maxResLvl = 0;
for(int c=0; c<nc; c++) {
mrl[c] = src.getAnSubbandTree(t,c).resLvl;
if(mrl[c]>maxResLvl) maxResLvl = mrl[c];
}
int minlys; // minimum layer start index of each component
for(int r=rs; r<re; r++) { //loop on resolution levels
if(r>maxResLvl) continue;
minlys = 100000;
for(int c=cs; c<ce; c++) {
if( r<lys[c].length && lys[c][r]<minlys ) {
minlys = lys[c][r];
}
}
for(int l=minlys; l<lye; l++) { //loop on layers
for(int c=cs; c<ce; c++) {//loop on components
if(r>=lys[c].length) continue;
if(l<lys[c][r]) continue;
// If no more decomposition levels for this component
if(r>mrl[c]) continue;
nPrec = numPrec[t][c][r].x*numPrec[t][c][r].y;
for(int p=0; p<nPrec; p++) { // loop on precincts
// set boolean sopUsed here (SOP markers)
sopUsed = ((String)wp.getSOP().getTileDef(t)).equals("true");
// set boolean ephUsed here (EPH markers)
ephUsed = ((String)wp.getEPH().getTileDef(t)).equals("true");
sb = src.getAnSubbandTree(t,c);
for(int i=mrl[c]; i>r; i--) {
sb = sb.subb_LL;
}
threshold = layers[l].rdThreshold;
findTruncIndices(l,c,r,t,sb,threshold,p);
hBuff = pktEnc.encodePacket(l+1,c,r,t,cblks[t][c][r],
truncIdxs[t][l][c][r],
hBuff,bBuff,p);
if(pktEnc.isPacketWritable()) {
bsWriter.writePacketHead(hBuff.getBuffer(),
hBuff.getLength(),
false,sopUsed,ephUsed);
bsWriter.writePacketBody(pktEnc.getLastBodyBuf(),
pktEnc.getLastBodyLen(),
false,pktEnc.isROIinPkt(),
pktEnc.getROILen());
}
} // End loop on precincts
} // End loop on components
} // End loop on layers
} // End loop on resolution levels
}
/**
* Write a piece of bit stream according to the
* LY_RES_COMP_POS_PROG progression mode and between given bounds
*
* @param t Tile index.
*
* @param rs First resolution level index.
*
* @param re Last resolution level index.
*
* @param cs First component index.
*
* @param ce Last component index.
*
* @param lys First layer index for each component and resolution.
*
* @param lye Index of the last layer.
* */
public void writeLyResCompPos(int t,int rs,int re,int cs,int ce,
int[][] lys,int lye) throws IOException {
boolean sopUsed; // Should SOP markers be used ?
boolean ephUsed; // Should EPH markers be used ?
int nc = src.getNumComps();
int mrl;
SubbandAn sb;
float threshold;
BitOutputBuffer hBuff = null;
byte[] bBuff = null;
int nPrec = 0;
int minlys = 100000; // minimum layer start index of each component
for(int c=cs; c<ce; c++) {
for(int r=0; r<lys.length; r++) {
if(lys[c]!=null && r<lys[c].length && lys[c][r]<minlys ) {
minlys = lys[c][r];
}
}
}
for(int l=minlys; l<lye; l++) { // loop on layers
for(int r=rs; r<re; r++) { // loop on resolution level
for(int c=cs; c<ce; c++) { // loop on components
mrl = src.getAnSubbandTree(t,c).resLvl;
if(r>mrl) continue;
if(r>=lys[c].length) continue;
if(l<lys[c][r]) continue;
nPrec = numPrec[t][c][r].x*numPrec[t][c][r].y;
for(int p=0; p<nPrec; p++) { // loop on precincts
// set boolean sopUsed here (SOP markers)
sopUsed = ((String)wp.getSOP().getTileDef(t)).equals("true");
// set boolean ephUsed here (EPH markers)
ephUsed = ((String)wp.getEPH().getTileDef(t)).equals("true");
sb = src.getAnSubbandTree(t,c);
for(int i=mrl; i>r; i--) {
sb = sb.subb_LL;
}
threshold = layers[l].rdThreshold;
findTruncIndices(l,c,r,t,sb,threshold,p);
hBuff = pktEnc.encodePacket(l+1,c,r,t,cblks[t][c][r],
truncIdxs[t][l][c][r],
hBuff,bBuff,p);
if(pktEnc.isPacketWritable()) {
bsWriter.writePacketHead(hBuff.getBuffer(),
hBuff.getLength(),
false,sopUsed,ephUsed);
bsWriter.writePacketBody(pktEnc.getLastBodyBuf(),
pktEnc.getLastBodyLen(),
false,pktEnc.isROIinPkt(),
pktEnc.getROILen());
}
} // end loop on precincts
} // end loop on components
} // end loop on resolution levels
} // end loop on layers
}
/**
* Write a piece of bit stream according to the
* COMP_POS_RES_LY_PROG progression mode and between given bounds
*
* @param t Tile index.
*
* @param rs First resolution level index.
*
* @param re Last resolution level index.
*
* @param cs First component index.
*
* @param ce Last component index.
*
* @param lys First layer index for each component and resolution.
*
* @param lye Index of the last layer.
* */
public void writePosCompResLy(int t,int rs,int re,int cs,int ce,
int[][] lys,int lye) throws IOException {
boolean sopUsed; // Should SOP markers be used ?
boolean ephUsed; // Should EPH markers be used ?
int nc = src.getNumComps();
int mrl;
SubbandAn sb;
float threshold;
BitOutputBuffer hBuff = null;
byte[] bBuff = null;
// Computes current tile offset in the reference grid
Point nTiles = src.getNumTiles(null);
Point tileI = src.getTile(null);
int x0siz = src.getImgULX();
int y0siz = src.getImgULY();
int xsiz = x0siz + src.getImgWidth();
int ysiz = y0siz + src.getImgHeight();
int xt0siz = src.getTilePartULX();
int yt0siz = src.getTilePartULY();
int xtsiz = src.getNomTileWidth();
int ytsiz = src.getNomTileHeight();
int tx0 = (tileI.x==0) ? x0siz : xt0siz+tileI.x*xtsiz;
int ty0 = (tileI.y==0) ? y0siz : yt0siz+tileI.y*ytsiz;
int tx1 = (tileI.x!=nTiles.x-1) ? xt0siz+(tileI.x+1)*xtsiz : xsiz;
int ty1 = (tileI.y!=nTiles.y-1) ? yt0siz+(tileI.y+1)*ytsiz : ysiz;
// Get precinct information (number,distance between two consecutive
// precincts in the reference grid) in each component and resolution
// level
PrecInfo prec; // temporary variable
int p; // Current precinct index
int gcd_x = 0; // Horiz. distance between 2 precincts in the ref. grid
int gcd_y = 0; // Vert. distance between 2 precincts in the ref. grid
int nPrec = 0; // Total number of found precincts
int[][] nextPrec = new int [ce][]; // Next precinct index in each
// component and resolution level
int minlys = 100000; // minimum layer start index of each component
int minx = tx1; // Horiz. offset of the second precinct in the
// reference grid
int miny = ty1; // Vert. offset of the second precinct in the
// reference grid.
int maxx = tx0; // Max. horiz. offset of precincts in the ref. grid
int maxy = ty0; // Max. vert. offset of precincts in the ref. grid
for(int c=cs; c<ce; c++) {
mrl = src.getAnSubbandTree(t,c).resLvl;
nextPrec[c] = new int[mrl+1];
for(int r=rs; r<re; r++) {
if(r>mrl) continue;
if (r<lys[c].length && lys[c][r]<minlys) {
minlys = lys[c][r];
}
p = numPrec[t][c][r].y*numPrec[t][c][r].x-1;
for(; p>=0; p--) {
prec = pktEnc.getPrecInfo(t,c,r,p);
if(prec.rgulx!=tx0) {
if(prec.rgulx<minx) minx = prec.rgulx;
if(prec.rgulx>maxx) maxx = prec.rgulx;
}
if(prec.rguly!=ty0){
if(prec.rguly<miny) miny = prec.rguly;
if(prec.rguly>maxy) maxy = prec.rguly;
}
if(nPrec==0) {
gcd_x = prec.rgw;
gcd_y = prec.rgh;
} else {
gcd_x = MathUtil.gcd(gcd_x,prec.rgw);
gcd_y = MathUtil.gcd(gcd_y,prec.rgh);
}
nPrec++;
} // precincts
} // resolution levels
} // components
if(nPrec==0) {
throw new Error("Image cannot have no precinct");
}
int pyend = (maxy-miny)/gcd_y+1;
int pxend = (maxx-minx)/gcd_x+1;
int y = ty0;
int x = tx0;
for(int py=0; py<=pyend; py++) { // Vertical precincts
for(int px=0; px<=pxend; px++) { // Horiz. precincts
for(int c=cs; c<ce; c++) { // Components
mrl = src.getAnSubbandTree(t,c).resLvl;
for(int r=rs; r<re; r++) { // Resolution levels
if(r>mrl) continue;
if(nextPrec[c][r] >=
numPrec[t][c][r].x*numPrec[t][c][r].y) {
continue;
}
prec = pktEnc.getPrecInfo(t,c,r,nextPrec[c][r]);
if((prec.rgulx!=x) || (prec.rguly!=y)) {
continue;
}
for(int l=minlys; l<lye; l++) { // Layers
if(r>=lys[c].length) continue;
if(l<lys[c][r]) continue;
// set boolean sopUsed here (SOP markers)
sopUsed = ((String)wp.getSOP().getTileDef(t)).equals("true");
// set boolean ephUsed here (EPH markers)
ephUsed = ((String)wp.getEPH().getTileDef(t)).equals("true");
sb = src.getAnSubbandTree(t,c);
for(int i=mrl; i>r; i--) {
sb = sb.subb_LL;
}
threshold = layers[l].rdThreshold;
findTruncIndices(l,c,r,t,sb,threshold,
nextPrec[c][r]);
hBuff = pktEnc.encodePacket(l+1,c,r,t,
cblks[t][c][r],
truncIdxs[t][l][c][r],
hBuff,bBuff,
nextPrec[c][r]);
if(pktEnc.isPacketWritable()) {
bsWriter.writePacketHead(hBuff.getBuffer(),
hBuff.getLength(),
false,sopUsed,
ephUsed);
bsWriter.writePacketBody(pktEnc.
getLastBodyBuf(),
pktEnc.
getLastBodyLen(),
false,
pktEnc.isROIinPkt(),
pktEnc.getROILen());
}
} // layers
nextPrec[c][r]++;
} // Resolution levels
} // Components
if(px!=pxend) {
x = minx+px*gcd_x;
} else {
x = tx0;
}
} // Horizontal precincts
if(py!=pyend) {
y = miny+py*gcd_y;
} else {
y = ty0;
}
} // Vertical precincts
// Check that all precincts have been written
for(int c=cs; c<ce; c++) {
mrl = src.getAnSubbandTree(t,c).resLvl;
for(int r=rs; r<re; r++) {
if(r>mrl) continue;
if(nextPrec[c][r]<numPrec[t][c][r].x*numPrec[t][c][r].y-1) {
throw new Error("JJ2000 bug: One precinct at least has "+
"not been written for resolution level "+r
+" of component "+c+" in tile "+t+".");
}
}
}
}
/**
* Write a piece of bit stream according to the
* COMP_POS_RES_LY_PROG progression mode and between given bounds
*
* @param t Tile index.
*
* @param rs First resolution level index.
*
* @param re Last resolution level index.
*
* @param cs First component index.
*
* @param ce Last component index.
*
* @param lys First layer index for each component and resolution.
*
* @param lye Index of the last layer.
* */
public void writeCompPosResLy(int t,int rs,int re,int cs,int ce,
int[][] lys,int lye) throws IOException {
boolean sopUsed; // Should SOP markers be used ?
boolean ephUsed; // Should EPH markers be used ?
int nc = src.getNumComps();
int mrl;
SubbandAn sb;
float threshold;
BitOutputBuffer hBuff = null;
byte[] bBuff = null;
// Computes current tile offset in the reference grid
Point nTiles = src.getNumTiles(null);
Point tileI = src.getTile(null);
int x0siz = src.getImgULX();
int y0siz = src.getImgULY();
int xsiz = x0siz + src.getImgWidth();
int ysiz = y0siz + src.getImgHeight();
int xt0siz = src.getTilePartULX();
int yt0siz = src.getTilePartULY();
int xtsiz = src.getNomTileWidth();
int ytsiz = src.getNomTileHeight();
int tx0 = (tileI.x==0) ? x0siz : xt0siz+tileI.x*xtsiz;
int ty0 = (tileI.y==0) ? y0siz : yt0siz+tileI.y*ytsiz;
int tx1 = (tileI.x!=nTiles.x-1) ? xt0siz+(tileI.x+1)*xtsiz : xsiz;
int ty1 = (tileI.y!=nTiles.y-1) ? yt0siz+(tileI.y+1)*ytsiz : ysiz;
// Get precinct information (number,distance between two consecutive
// precincts in the reference grid) in each component and resolution
// level
PrecInfo prec; // temporary variable
int p; // Current precinct index
int gcd_x = 0; // Horiz. distance between 2 precincts in the ref. grid
int gcd_y = 0; // Vert. distance between 2 precincts in the ref. grid
int nPrec = 0; // Total number of found precincts
int[][] nextPrec = new int [ce][]; // Next precinct index in each
// component and resolution level
int minlys = 100000; // minimum layer start index of each component
int minx = tx1; // Horiz. offset of the second precinct in the
// reference grid
int miny = ty1; // Vert. offset of the second precinct in the
// reference grid.
int maxx = tx0; // Max. horiz. offset of precincts in the ref. grid
int maxy = ty0; // Max. vert. offset of precincts in the ref. grid
for(int c=cs; c<ce; c++) {
mrl = src.getAnSubbandTree(t,c).resLvl;
for(int r=rs; r<re; r++) {
if(r>mrl) continue;
nextPrec[c] = new int[mrl+1];
if (r<lys[c].length && lys[c][r]<minlys) {
minlys = lys[c][r];
}
p = numPrec[t][c][r].y*numPrec[t][c][r].x-1;
for(; p>=0; p--) {
prec = pktEnc.getPrecInfo(t,c,r,p);
if(prec.rgulx!=tx0) {
if(prec.rgulx<minx) minx = prec.rgulx;
if(prec.rgulx>maxx) maxx = prec.rgulx;
}
if(prec.rguly!=ty0){
if(prec.rguly<miny) miny = prec.rguly;
if(prec.rguly>maxy) maxy = prec.rguly;
}
if(nPrec==0) {
gcd_x = prec.rgw;
gcd_y = prec.rgh;
} else {
gcd_x = MathUtil.gcd(gcd_x,prec.rgw);
gcd_y = MathUtil.gcd(gcd_y,prec.rgh);
}
nPrec++;
} // precincts
} // resolution levels
} // components
if(nPrec==0) {
throw new Error("Image cannot have no precinct");
}
int pyend = (maxy-miny)/gcd_y+1;
int pxend = (maxx-minx)/gcd_x+1;
int y;
int x;
for(int c=cs; c<ce; c++) { // Loop on components
y = ty0;
x = tx0;
mrl = src.getAnSubbandTree(t,c).resLvl;
for(int py=0; py<=pyend; py++) { // Vertical precincts
for(int px=0; px<=pxend; px++) { // Horiz. precincts
for(int r=rs; r<re; r++) { // Resolution levels
if(r>mrl) continue;
if(nextPrec[c][r] >=
numPrec[t][c][r].x*numPrec[t][c][r].y) {
continue;
}
prec = pktEnc.getPrecInfo(t,c,r,nextPrec[c][r]);
if((prec.rgulx!=x) || (prec.rguly!=y)) {
continue;
}
for(int l=minlys; l<lye; l++) { // Layers
if(r>=lys[c].length) continue;
if(l<lys[c][r]) continue;
// set boolean sopUsed here (SOP markers)
sopUsed = ((String)wp.getSOP().getTileDef(t)).equals("true");
// set boolean ephUsed here (EPH markers)
ephUsed = ((String)wp.getEPH().getTileDef(t)).equals("true");
sb = src.getAnSubbandTree(t,c);
for(int i=mrl; i>r; i--) {
sb = sb.subb_LL;
}
threshold = layers[l].rdThreshold;
findTruncIndices(l,c,r,t,sb,threshold,
nextPrec[c][r]);
hBuff = pktEnc.encodePacket(l+1,c,r,t,
cblks[t][c][r],
truncIdxs[t][l][c][r],
hBuff,bBuff,
nextPrec[c][r]);
if(pktEnc.isPacketWritable()) {
bsWriter.writePacketHead(hBuff.getBuffer(),
hBuff.getLength(),
false,sopUsed,
ephUsed);
bsWriter.writePacketBody(pktEnc.
getLastBodyBuf(),
pktEnc.
getLastBodyLen(),
false,
pktEnc.isROIinPkt(),
pktEnc.getROILen());
}
} // Layers
nextPrec[c][r]++;
} // Resolution levels
if(px!=pxend) {
x = minx+px*gcd_x;
} else {
x = tx0;
}
} // Horizontal precincts
if(py!=pyend) {
y = miny+py*gcd_y;
} else {
y = ty0;
}
} // Vertical precincts
} // components
// Check that all precincts have been written
for(int c=cs; c<ce; c++) {
mrl = src.getAnSubbandTree(t,c).resLvl;
for(int r=rs; r<re; r++) {
if(r>mrl) continue;
if(nextPrec[c][r]<numPrec[t][c][r].x*numPrec[t][c][r].y-1) {
throw new Error("JJ2000 bug: One precinct at least has "+
"not been written for resolution level "+r
+" of component "+c+" in tile "+t+".");
}
}
}
}
/**
* Write a piece of bit stream according to the
* RES_POS_COMP_LY_PROG progression mode and between given bounds
*
* @param t Tile index.
*
* @param rs First resolution level index.
*
* @param re Last resolution level index.
*
* @param cs First component index.
*
* @param ce Last component index.
*
* @param lys First layer index for each component and resolution.
*
* @param lye Last layer index.
* */
public void writeResPosCompLy(int t,int rs,int re,int cs,int ce,
int[][] lys,int lye) throws IOException {
boolean sopUsed; // Should SOP markers be used ?
boolean ephUsed; // Should EPH markers be used ?
int nc = src.getNumComps();
int mrl;
SubbandAn sb;
float threshold;
BitOutputBuffer hBuff = null;
byte[] bBuff = null;
// Computes current tile offset in the reference grid
Point nTiles = src.getNumTiles(null);
Point tileI = src.getTile(null);
int x0siz = src.getImgULX();
int y0siz = src.getImgULY();
int xsiz = x0siz + src.getImgWidth();
int ysiz = y0siz + src.getImgHeight();
int xt0siz = src.getTilePartULX();
int yt0siz = src.getTilePartULY();
int xtsiz = src.getNomTileWidth();
int ytsiz = src.getNomTileHeight();
int tx0 = (tileI.x==0) ? x0siz : xt0siz+tileI.x*xtsiz;
int ty0 = (tileI.y==0) ? y0siz : yt0siz+tileI.y*ytsiz;
int tx1 = (tileI.x!=nTiles.x-1) ? xt0siz+(tileI.x+1)*xtsiz : xsiz;
int ty1 = (tileI.y!=nTiles.y-1) ? yt0siz+(tileI.y+1)*ytsiz : ysiz;
// Get precinct information (number,distance between two consecutive
// precincts in the reference grid) in each component and resolution
// level
PrecInfo prec; // temporary variable
int p; // Current precinct index
int gcd_x = 0; // Horiz. distance between 2 precincts in the ref. grid
int gcd_y = 0; // Vert. distance between 2 precincts in the ref. grid
int nPrec = 0; // Total number of found precincts
int[][] nextPrec = new int [ce][]; // Next precinct index in each
// component and resolution level
int minlys = 100000; // minimum layer start index of each component
int minx = tx1; // Horiz. offset of the second precinct in the
// reference grid
int miny = ty1; // Vert. offset of the second precinct in the
// reference grid.
int maxx = tx0; // Max. horiz. offset of precincts in the ref. grid
int maxy = ty0; // Max. vert. offset of precincts in the ref. grid
for(int c=cs; c<ce; c++) {
mrl = src.getAnSubbandTree(t,c).resLvl;
nextPrec[c] = new int[mrl+1];
for(int r=rs; r<re; r++) {
if(r>mrl) continue;
if (r<lys[c].length && lys[c][r]<minlys) {
minlys = lys[c][r];
}
p = numPrec[t][c][r].y*numPrec[t][c][r].x-1;
for(; p>=0; p--) {
prec = pktEnc.getPrecInfo(t,c,r,p);
if(prec.rgulx!=tx0) {
if(prec.rgulx<minx) minx = prec.rgulx;
if(prec.rgulx>maxx) maxx = prec.rgulx;
}
if(prec.rguly!=ty0){
if(prec.rguly<miny) miny = prec.rguly;
if(prec.rguly>maxy) maxy = prec.rguly;
}
if(nPrec==0) {
gcd_x = prec.rgw;
gcd_y = prec.rgh;
} else {
gcd_x = MathUtil.gcd(gcd_x,prec.rgw);
gcd_y = MathUtil.gcd(gcd_y,prec.rgh);
}
nPrec++;
} // precincts
} // resolution levels
} // components
if(nPrec==0) {
throw new Error("Image cannot have no precinct");
}
int pyend = (maxy-miny)/gcd_y+1;
int pxend = (maxx-minx)/gcd_x+1;
int x,y;
for(int r=rs; r<re; r++) { // Resolution levels
y = ty0;
x = tx0;
for(int py=0; py<=pyend; py++) { // Vertical precincts
for(int px=0; px<=pxend; px++) { // Horiz. precincts
for(int c=cs; c<ce; c++) { // Components
mrl = src.getAnSubbandTree(t,c).resLvl;
if(r>mrl) continue;
if(nextPrec[c][r] >=
numPrec[t][c][r].x*numPrec[t][c][r].y) {
continue;
}
prec = pktEnc.getPrecInfo(t,c,r,nextPrec[c][r]);
if((prec.rgulx!=x) || (prec.rguly!=y)) {
continue;
}
for(int l=minlys; l<lye; l++) {
if(r>=lys[c].length) continue;
if(l<lys[c][r]) continue;
// set boolean sopUsed here (SOP markers)
sopUsed = ((String)wp.getSOP().getTileDef(t)).equals("true");
// set boolean ephUsed here (EPH markers)
ephUsed = ((String)wp.getEPH().getTileDef(t)).equals("true");
sb = src.getAnSubbandTree(t,c);
for(int i=mrl; i>r; i--) {
sb = sb.subb_LL;
}
threshold = layers[l].rdThreshold;
findTruncIndices(l,c,r,t,sb,threshold,
nextPrec[c][r]);
hBuff = pktEnc.encodePacket(l+1,c,r,t,
cblks[t][c][r],
truncIdxs[t][l][c][r],
hBuff,bBuff,
nextPrec[c][r]);
if(pktEnc.isPacketWritable()) {
bsWriter.writePacketHead(hBuff.getBuffer(),
hBuff.getLength(),
false,sopUsed,
ephUsed);
bsWriter.writePacketBody(pktEnc.
getLastBodyBuf(),
pktEnc.
getLastBodyLen(),
false,
pktEnc.isROIinPkt(),
pktEnc.getROILen());
}
} // layers
nextPrec[c][r]++;
} // Components
if(px!=pxend) {
x = minx+px*gcd_x;
} else {
x = tx0;
}
} // Horizontal precincts
if(py!=pyend) {
y = miny+py*gcd_y;
} else {
y = ty0;
}
} // Vertical precincts
} // Resolution levels
// Check that all precincts have been written
for(int c=cs; c<ce; c++) {
mrl = src.getAnSubbandTree(t,c).resLvl;
for(int r=rs; r<re; r++) {
if(r>mrl) continue;
if(nextPrec[c][r]<numPrec[t][c][r].x*numPrec[t][c][r].y-1) {
throw new Error("JJ2000 bug: One precinct at least has "+
"not been written for resolution level "+r
+" of component "+c+" in tile "+t+".");
}
}
}
}
/**
* This function implements the rate-distortion optimization algorithm.
* It saves the state of any previously generated bit-stream layers and
* then simulate the formation of a new layer in the bit stream as often
* as necessary to find the smallest rate-distortion threshold such that
* the total number of bytes required to represent the layer does not
* exceed `maxBytes' minus `prevBytes'. It then restores the state of any
* previously generated bit-stream layers and returns the threshold.
*
* @param layerIdx The index of the current layer
*
* @param fmaxt The maximum admissible slope value. Normally the threshold
* slope of the previous layer.
*
* @param maxBytes The maximum number of bytes that can be written. It
* includes the length of the current layer bistream length and all the
* previous layers bit streams.
*
* @param prevBytes The number of bytes of all the previous layers.
*
* @return The value of the slope threshold.
* */
private float optimizeBitstreamLayer (int layerIdx, float fmaxt,
int maxBytes, int prevBytes)
throws IOException {
int nt; // The total number of tiles
int nc; // The total number of components
int numLvls; // The total number of resolution levels
int actualBytes; // Actual number of bytes for a layer
float fmint; // Minimum of the current threshold interval
float ft; // Current threshold
SubbandAn sb; // Current subband
BitOutputBuffer hBuff;// The packet head buffer
byte[] bBuff; // The packet body buffer
int sidx; // The index in the summary table
boolean sopUsed; // Should SOP markers be used ?
boolean ephUsed; // Should EPH markers be used ?
int precinctIdx; // Precinct index for current packet
int nPrec; // Number of precincts in the current resolution level
// Save the packet encoder state
pktEnc.save();
nt = src.getNumTiles();
nc = src.getNumComps();
hBuff = null;
bBuff = null;
// Estimate the minimum slope to start with from the summary
// information in 'RDSlopesRates'. This is a real minimum since it
// does not include the packet head overhead, which is always
// non-zero.
// Look for the summary entry that gives 'maxBytes' or more data
for (sidx=RD_SUMMARY_SIZE-1; sidx > 0; sidx--) {
if (RDSlopesRates[sidx] >= maxBytes) {
break;
}
}
// Get the corresponding minimum slope
fmint = getSlopeFromSIndex(sidx);
// Ensure that it is smaller the maximum slope
if (fmint>=fmaxt) {
sidx--;
fmint = getSlopeFromSIndex(sidx);
}
// If we are using the last entry of the summary, then that
// corresponds to all the data, Thus, set the minimum slope to 0.
if (sidx<=0) fmint = 0;
// We look for the best threshold 'ft', which is the lowest threshold
// that generates no more than 'maxBytes' code bytes.
// The search is done iteratively using a binary split algorithm. We
// start with 'fmaxt' as the maximum possible threshold, and 'fmint'
// as the minimum threshold. The threshold 'ft' is calculated as the
// middle point of 'fmaxt'-'fmint' interval. The 'fmaxt' or 'fmint'
// bounds are moved according to the number of bytes obtained from a
// simulation, where 'ft' is used as the threshold.
// We stop whenever the interval is sufficiently small, and thus
// enough precision is achieved.
// Initialize threshold as the middle point of the interval.
ft = (fmaxt+fmint)/2f;
// If 'ft' reaches 'fmint' it means that 'fmaxt' and 'fmint' are so
// close that the average is 'fmint', due to rounding. Force it to
// 'fmaxt' instead, since 'fmint' is normally an exclusive lower
// bound.
if (ft <= fmint) ft = fmaxt;
do {
// Get the number of bytes used by this layer, if 'ft' is the
// threshold, by simulation.
actualBytes = prevBytes;
src.setTile(0,0);
for (int t=0; t<nt; t++){
for (int c=0; c<nc; c++) {
// set boolean sopUsed here (SOP markers)
sopUsed = ((String)wp.getSOP().getTileDef(t)).equalsIgnoreCase("true");
// set boolean ephUsed here (EPH markers)
ephUsed = ((String)wp.getEPH().getTileDef(t)).equalsIgnoreCase("true");
// Get LL subband
sb = (SubbandAn) src.getAnSubbandTree(t,c);
numLvls = sb.resLvl + 1;
sb = (SubbandAn) sb.getSubbandByIdx(0,0);
//loop on resolution levels
for(int r=0; r<numLvls; r++) {
nPrec = numPrec[t][c][r].x*numPrec[t][c][r].y;
for(int p=0; p<nPrec; p++) {
findTruncIndices(layerIdx,c,r,t,sb,ft,p);
hBuff = pktEnc.encodePacket(layerIdx+1,c,r,t,
cblks[t][c][r],
truncIdxs[t][layerIdx]
[c][r],hBuff,bBuff,p);
if(pktEnc.isPacketWritable()) {
bBuff = pktEnc.getLastBodyBuf();
actualBytes += bsWriter.
writePacketHead(hBuff.getBuffer(),
hBuff.getLength(),
true, sopUsed,ephUsed);
actualBytes += bsWriter.
writePacketBody(bBuff,
pktEnc.getLastBodyLen(),
true,pktEnc.isROIinPkt(),
pktEnc.getROILen());
}
} // end loop on precincts
sb = sb.parent;
} // End loop on resolution levels
} // End loop on components
} // End loop on tiles
// Move the interval bounds according to simulation result
if (actualBytes>maxBytes) {
// 'ft' is too low and generates too many bytes, make it the
// new minimum.
fmint = ft;
} else {
// 'ft' is too high and does not generate as many bytes as we
// are allowed too, make it the new maximum.
fmaxt = ft;
}
// Update 'ft' for the new iteration as the middle point of the
// new interval.
ft = (fmaxt+fmint)/2f;
// If 'ft' reaches 'fmint' it means that 'fmaxt' and 'fmint' are
// so close that the average is 'fmint', due to rounding. Force it
// to 'fmaxt' instead, since 'fmint' is normally an exclusive
// lower bound.
if (ft <= fmint) ft = fmaxt;
// Restore previous packet encoder state
pktEnc.restore();
// We continue to iterate, until the threshold reaches the upper
// limit of the interval, within a FLOAT_REL_PRECISION relative
// tolerance, or a FLOAT_ABS_PRECISION absolute tolerance. This is
// the sign that the interval is sufficiently small.
} while (ft < fmaxt*(1f-FLOAT_REL_PRECISION) &&
ft < (fmaxt-FLOAT_ABS_PRECISION));
// If we have a threshold which is close to 0, set it to 0 so that
// everything is taken into the layer. This is to avoid not sending
// some least significant bit-planes in the lossless case. We use the
// FLOAT_ABS_PRECISION value as a measure of "close" to 0.
if (ft <= FLOAT_ABS_PRECISION) {
ft = 0f;
} else {
// Otherwise make the threshold 'fmaxt', just to be sure that we
// will not send more bytes than allowed.
ft = fmaxt;
}
return ft;
}
/**
* This function attempts to estimate a rate-distortion slope threshold
* which will achieve a target number of code bytes close the
* `targetBytes' value.
*
* @param targetBytes The target number of bytes for the current layer
*
* @param lastLayer The previous layer information.
*
* @return The value of the slope threshold for the estimated layer
* */
private float estimateLayerThreshold(int targetBytes,
EBCOTLayer lastLayer) {
float log_sl1; // The log of the first slope used for interpolation
float log_sl2; // The log of the second slope used for interpolation
float log_len1; // The log of the first length used for interpolation
float log_len2; // The log of the second length used for interpolation
float log_isl; // The log of the interpolated slope
float log_ilen; // Log of the interpolated length
float log_ab; // Log of actual bytes in last layer
int sidx; // Index into the summary R-D info array
float log_off; // The log of the offset proportion
int tlen; // The corrected target layer length
float lthresh; // The threshold of the last layer
float eth; // The estimated threshold
// In order to estimate the threshold we base ourselves in the summary
// R-D info in RDSlopesRates. In order to use it we must compensate
// for the overhead of the packet heads. The proportion of overhead is
// estimated using the last layer simulation results.
// NOTE: the model used in this method is that the slope varies
// linearly with the log of the rate (i.e. length).
// NOTE: the model used in this method is that the distortion is
// proprotional to a power of the rate. Thus, the slope is also
// proportional to another power of the rate. This translates as the
// log of the slope varies linearly with the log of the rate, which is
// what we use.
// 1) Find the offset of the length predicted from the summary R-D
// information, to the actual length by using the last layer.
// We ensure that the threshold we use for estimation actually
// includes some data.
lthresh = lastLayer.rdThreshold;
if (lthresh > maxSlope) lthresh = maxSlope;
// If the slope of the last layer is too small then we just include
// all the rest (not possible to do better).
if (lthresh < FLOAT_ABS_PRECISION) return 0f;
sidx = getLimitedSIndexFromSlope(lthresh);
// If the index is outside of the summary info array use the last two,
// or first two, indexes, as appropriate
if (sidx >= RD_SUMMARY_SIZE-1) sidx = RD_SUMMARY_SIZE-2;
// Get the logs of the lengths and the slopes
if (RDSlopesRates[sidx+1] == 0) {
// Pathological case, we can not use log of 0. Add
// RDSlopesRates[sidx]+1 bytes to the rates (just a crude simple
// solution to this rare case)
log_len1 = (float)Math.log((RDSlopesRates[sidx]<<1)+1);
log_len2 = (float)Math.log(RDSlopesRates[sidx]+1);
log_ab = (float)Math.log(lastLayer.actualBytes+
RDSlopesRates[sidx]+1);
}
else {
log_len1 = (float)Math.log(RDSlopesRates[sidx]);
log_len2 = (float)Math.log(RDSlopesRates[sidx+1]);
log_ab = (float)Math.log(lastLayer.actualBytes);
}
log_sl1 = (float)Math.log(getSlopeFromSIndex(sidx));
log_sl2 = (float)Math.log(getSlopeFromSIndex(sidx+1));
log_isl = (float)Math.log(lthresh);
log_ilen = log_len1 +
(log_isl-log_sl1)*(log_len1-log_len2)/(log_sl1-log_sl2);
log_off = log_ab - log_ilen;
// Do not use negative offsets (i.e. offset proportion larger than 1)
// since that is probably a sign that our model is off. To be
// conservative use an offset of 0 (i.e. offset proportiojn 1).
if (log_off < 0) log_off = 0f;
// 2) Correct the target layer length by the offset.
tlen = (int)(targetBytes/(float)Math.exp(log_off));
// 3) Find, from the summary R-D info, the thresholds that generate
// lengths just above and below our corrected target layer length.
// Look for the index in the summary info array that gives the largest
// length smaller than the target length
for (sidx = RD_SUMMARY_SIZE-1; sidx>=0 ; sidx--) {
if (RDSlopesRates[sidx] >= tlen) break;
}
sidx++;
// Correct if out of the array
if (sidx >= RD_SUMMARY_SIZE) sidx = RD_SUMMARY_SIZE-1;
if (sidx <= 0) sidx = 1;
// Get the log of the lengths and the slopes that are just above and
// below the target length.
if (RDSlopesRates[sidx] == 0) {
// Pathological case, we can not use log of 0. Add
// RDSlopesRates[sidx-1]+1 bytes to the rates (just a crude simple
// solution to this rare case)
log_len1 = (float)Math.log(RDSlopesRates[sidx-1]+1);
log_len2 = (float)Math.log((RDSlopesRates[sidx-1]<<1)+1);
log_ilen = (float)Math.log(tlen+RDSlopesRates[sidx-1]+1);
}
else {
// Normal case, we can safely take the logs.
log_len1 = (float)Math.log(RDSlopesRates[sidx]);
log_len2 = (float)Math.log(RDSlopesRates[sidx-1]);
log_ilen = (float)Math.log(tlen);
}
log_sl1 = (float)Math.log(getSlopeFromSIndex(sidx));
log_sl2 = (float)Math.log(getSlopeFromSIndex(sidx-1));
// 4) Interpolate the two thresholds to find the target threshold.
log_isl = log_sl1 +
(log_ilen-log_len1)*(log_sl1-log_sl2)/(log_len1-log_len2);
eth = (float)Math.exp(log_isl);
// Correct out of bounds results
if (eth > lthresh) eth = lthresh;
if (eth < FLOAT_ABS_PRECISION) eth = 0f;
// Return the estimated threshold
return eth;
}
/**
* This function finds the new truncation points indices for a packet. It
* does so by including the data from the code-blocks in the component,
* resolution level and tile, associated with a R-D slope which is larger
* than or equal to 'fthresh'.
*
* @param layerIdx The index of the current layer
*
* @param compIdx The index of the current component
*
* @param lvlIdx The index of the current resolution level
*
* @param tileIdx The index of the current tile
*
* @param subb The LL subband in the resolution level lvlIdx, which is
* parent of all the subbands in the packet. Except for resolution level 0
* this subband is always a node.
*
* @param fthresh The value of the rate-distortion threshold
* */
private void findTruncIndices(int layerIdx, int compIdx, int lvlIdx,
int tileIdx, SubbandAn subb,
float fthresh, int precinctIdx) {
int minsbi, maxsbi, b, bIdx, n;
Point ncblks = null;
SubbandAn sb;
CBlkRateDistStats cur_cblk;
PrecInfo prec = pktEnc.getPrecInfo(tileIdx,compIdx,lvlIdx,precinctIdx);
Point cbCoord;
sb = subb;
while(sb.subb_HH != null) {
sb = sb.subb_HH;
}
minsbi = (lvlIdx==0) ? 0 : 1;
maxsbi = (lvlIdx==0) ? 1 : 4;
int yend,xend;
sb = (SubbandAn)subb.getSubbandByIdx(lvlIdx, minsbi);
for(int s=minsbi; s<maxsbi; s++) { //loop on subbands
yend = (prec.cblk[s]!=null) ? prec.cblk[s].length : 0;
for(int y=0; y<yend; y++) {
xend = (prec.cblk[s][y]!=null) ? prec.cblk[s][y].length : 0;
for (int x=0; x<xend; x++) {
cbCoord = prec.cblk[s][y][x].idx;
b = cbCoord.x+cbCoord.y*sb.numCb.x;
//Get the current code-block
cur_cblk = cblks[tileIdx][compIdx][lvlIdx][s][b];
for(n=0; n<cur_cblk.nVldTrunc; n++) {
if(cur_cblk.truncSlopes[n] < fthresh) {
break;
} else {
continue;
}
}
// Store the index in the code-block truncIdxs that gives
// the real truncation index.
truncIdxs[tileIdx][layerIdx][compIdx][lvlIdx][s][b] = n-1;
} // End loop on horizontal code-blocks
} // End loop on vertical code-blocks
sb = (SubbandAn)sb.nextSubband();
} // End loop on subbands
}
/**
* Returns the index of a slope for the summary table, limiting to the
* admissible values. The index is calculated as RD_SUMMARY_OFF plus the
* maximum exponent, base 2, that yields a value not larger than the slope
* itself.
*
* <P>If the value to return is lower than 0, 0 is returned. If it is
* larger than the maximum table index, then the maximum is returned.
*
* @param slope The slope value
*
* @return The index for the summary table of the slope.
* */
private static int getLimitedSIndexFromSlope(float slope) {
int idx;
idx = (int)Math.floor(Math.log(slope)/LOG2) + RD_SUMMARY_OFF;
if (idx < 0) {
return 0;
}
else if (idx >= RD_SUMMARY_SIZE) {
return RD_SUMMARY_SIZE-1;
}
else {
return idx;
}
}
/**
* Returns the minimum slope value associated with a summary table
* index. This minimum slope is just 2^(index-RD_SUMMARY_OFF).
*
* @param index The summary index value.
*
* @return The minimum slope value associated with a summary table index.
* */
private static float getSlopeFromSIndex(int index) {
return (float)Math.pow(2,(index-RD_SUMMARY_OFF));
}
}