001/* 002 * $RCSfile: PostCompRateAllocator.java,v $ 003 * $Revision: 1.1 $ 004 * $Date: 2005/02/11 05:02:09 $ 005 * $State: Exp $ 006 * 007 * Class: PostCompRateAllocator 008 * 009 * Description: Generic interface for post-compression 010 * rate allocator. 011 * 012 * 013 * 014 * COPYRIGHT: 015 * 016 * This software module was originally developed by Raphaël Grosbois and 017 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel 018 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David 019 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research 020 * Centre France S.A) in the course of development of the JPEG2000 021 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This 022 * software module is an implementation of a part of the JPEG 2000 023 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio 024 * Systems AB and Canon Research Centre France S.A (collectively JJ2000 025 * Partners) agree not to assert against ISO/IEC and users of the JPEG 026 * 2000 Standard (Users) any of their rights under the copyright, not 027 * including other intellectual property rights, for this software module 028 * with respect to the usage by ISO/IEC and Users of this software module 029 * or modifications thereof for use in hardware or software products 030 * claiming conformance to the JPEG 2000 Standard. Those intending to use 031 * this software module in hardware or software products are advised that 032 * their use may infringe existing patents. The original developers of 033 * this software module, JJ2000 Partners and ISO/IEC assume no liability 034 * for use of this software module or modifications thereof. No license 035 * or right to this software module is granted for non JPEG 2000 Standard 036 * conforming products. JJ2000 Partners have full right to use this 037 * software module for his/her own purpose, assign or donate this 038 * software module to any third party and to inhibit third parties from 039 * using this software module for non JPEG 2000 Standard conforming 040 * products. This copyright notice must be included in all copies or 041 * derivative works of this software module. 042 * 043 * Copyright (c) 1999/2000 JJ2000 Partners. 044 * */ 045package jj2000.j2k.entropy.encoder; 046 047import java.io.IOException; 048import java.io.StreamTokenizer; 049import java.io.StringReader; 050 051import jj2000.j2k.codestream.ProgressionType; 052import jj2000.j2k.codestream.writer.CodestreamWriter; 053import jj2000.j2k.codestream.writer.HeaderEncoder; 054import jj2000.j2k.image.ImgDataAdapter; 055 056import com.github.jaiimageio.jpeg2000.impl.J2KImageWriteParamJava; 057/** 058 * This is the abstract class from which post-compression rate allocators 059 * which generate layers should inherit. The source of data is a 060 * 'CodedCBlkDataSrcEnc' which delivers entropy coded blocks with 061 * rate-distortion statistics. 062 * 063 * <P>The post compression rate allocator implementation should create the 064 * layers, according to a rate allocation policy, and send the packets to a 065 * CodestreamWriter. Since the rate allocator sends the packets to the bit 066 * stream then it should output the packets to the bit stream in the order 067 * imposed by the bit stream profiles. 068 * 069 * @see CodedCBlkDataSrcEnc 070 * 071 * @see jj2000.j2k.codestream.writer.CodestreamWriter 072 * */ 073public abstract class PostCompRateAllocator extends ImgDataAdapter { 074 075 /** The prefix for rate allocation options: 'A' */ 076 public final static char OPT_PREFIX = 'A'; 077 078 /** The list of parameters that is accepted for entropy coding. Options 079 * for entropy coding start with 'R'. */ 080 private final static String [][] pinfo = { 081 { "Aptype", "[<tile idx>] res|layer|res-pos|"+ 082 "pos-comp|comp-pos [res_start comp_start layer_end res_end "+ 083 "comp_end "+ 084 "prog] [[res_start comp_start ly_end res_end comp_end prog] ...] ["+ 085 "[<tile-component idx>] ...]", 086 "Specifies which type of progression should be used when "+ 087 "generating "+ 088 "the codestream. The 'res' value generates a resolution "+ 089 "progressive codestream with the number of layers specified by "+ 090 "'Alayers' option. The 'layer' value generates a layer progressive "+ 091 "codestream with multiple layers. In any case the rate-allocation "+ 092 "algorithm optimizes for best quality in each layer. The quality "+ 093 "measure is mean squared error (MSE) or a weighted version of it "+ 094 "(WMSE). If no progression type is specified or imposed by other "+ 095 "modules, the default value is 'layer'.\n"+ 096 "It is also possible to describe progression order changes. In "+ 097 "this case, 'res_start' is the index (from 0) of the first "+ 098 "resolution "+ 099 "level, 'comp_start' is the index (from 0) of the first component, "+ 100 "'ly_end' is the index (from 0) of the first layer not included, "+ 101 "'res_end' is the index (from 0) of the first resolution level not "+ 102 "included, 'comp_end' is index (from 0) of the first component not "+ 103 "included and 'prog' is the progression type to be used "+ 104 "for the rest of the tile/image. Several progression order changes "+ 105 "can be specified, one after the other." 106 , null}, 107 { "Alayers", "<rate> [+<layers>] [<rate [+<layers>] [...]]", 108 "Explicitly specifies the codestream layer formation parameters. "+ 109 "The <rate> parameter specifies the bitrate to which the first "+ 110 "layer should be optimized. The <layers> parameter, if present, "+ 111 "specifies the number of extra layers that should be added for "+ 112 "scalability. These extra layers are not optimized. "+ 113 "Any extra <rate> and <layers> parameters add more layers, in the "+ 114 "same way. An additional layer is always added at the end, which"+ 115 " is "+ 116 "optimized to the overall target bitrate of the bit stream. Any "+ 117 "layers (optimized or not) whose target bitrate is higher that the "+ 118 "overall target bitrate are silently ignored. The bitrates of the "+ 119 "extra layers that are added through the <layers> parameter are "+ 120 "approximately log-spaced between the other target bitrates. If "+ 121 "several <rate> [+<layers>] constructs appear the <rate>"+ 122 " parameters "+ 123 "must appear in increasing order. The rate allocation algorithm "+ 124 "ensures that all coded layers have a minimal reasonable size, if "+ 125 "not these layers are silently ignored.","0.015 +20 2.0 +10"} 126 }; 127 128 /** The source of entropy coded data */ 129 protected CodedCBlkDataSrcEnc src; 130 131 /** The source of entropy coded data */ 132 protected J2KImageWriteParamJava wp; 133 134 /** The number of layers. */ 135 protected int numLayers; 136 137 /** The bit-stream writer */ 138 CodestreamWriter bsWriter; 139 140 /** The header encoder */ 141 HeaderEncoder headEnc; 142 143 /** 144 * Initializes the source of entropy coded data. 145 * 146 * @param src The source of entropy coded data. 147 * 148 * @param ln The number of layers to create 149 * 150 * @param pt The Progression type, as defined in 'ProgressionType'. 151 * 152 * @param bw The packet bit stream writer. 153 * 154 * @see ProgressionType 155 * */ 156 public PostCompRateAllocator(CodedCBlkDataSrcEnc src, int nl, 157 CodestreamWriter bw, J2KImageWriteParamJava wp) { 158 super(src); 159 this.src = src; 160 this.wp = wp; 161 numLayers = nl; 162 bsWriter = bw; 163 } 164 165 /** 166 * Keep a reference to the header encoder. 167 * 168 * @param headEnc The header encoder 169 * */ 170 public void setHeaderEncoder(HeaderEncoder headEnc){ 171 this.headEnc = headEnc; 172 } 173 174 /** 175 * Initializes the rate allocation points, taking into account header 176 * overhead and such. This method must be called after the header has been 177 * simulated but before calling the runAndWrite() one. The header must be 178 * rewritten after a call to this method since the number of layers may 179 * change. 180 * 181 * @param oldSyntax Whether or not the old syntax is used. 182 * 183 * @see #runAndWrite 184 * */ 185 public abstract void initialize() throws IOException; 186 187 /** 188 * Runs the rate allocation algorithm and writes the data to the 189 * bit stream. This must be called after the initialize() method. 190 * 191 * @see #initialize 192 * */ 193 public abstract void runAndWrite() throws IOException; 194 195 /** 196 * Returns the number of layers that are actually generated. 197 * 198 * @return The number of layers generated. 199 * */ 200 public int getNumLayers() { 201 return numLayers; 202 } 203 204 /** 205 * Returns the parameters that are used in this class and implementing 206 * classes. It returns a 2D String array. Each of the 1D arrays is for a 207 * different option, and they have 3 elements. The first element is the 208 * option name, the second one is the synopsis, the third one is a long 209 * description of what the parameter is and the fourth is its default 210 * value. The synopsis or description may be 'null', in which case it is 211 * assumed that there is no synopsis or description of the option, 212 * respectively. Null may be returned if no options are supported. 213 * 214 * @return the options name, their synopsis and their explanation, 215 * or null if no options are supported. 216 * */ 217 public static String[][] getParameterInfo() { 218 return pinfo; 219 } 220 221 /** 222 * Creates a PostCompRateAllocator object for the appropriate rate 223 * allocation parameters in the parameter list 'pl', having 'src' as the 224 * source of entropy coded data, 'rate' as the target bitrate and 'bw' as 225 * the bit stream writer object. 226 * 227 * @param src The source of entropy coded data. 228 * 229 * @param pl The parameter lis (or options). 230 * 231 * @param rate The target bitrate for the rate allocation 232 * 233 * @param bw The bit stream writer object, where the bit stream data will 234 * be written. 235 * */ 236 public static PostCompRateAllocator createInstance(CodedCBlkDataSrcEnc src, 237 float rate, 238 CodestreamWriter bw, 239 J2KImageWriteParamJava wp){ 240 String lyropt = wp.getLayers(); 241 if (lyropt == null) { 242 if(wp.getROIs().getSpecified() == null) { 243 lyropt = "res"; 244 } 245 else { 246 lyropt = "layer"; 247 } 248 } 249 250 // Construct the layer specification from the Alayers option 251 LayersInfo lyrs = parseAlayers(lyropt,rate); 252 253 int nTiles = wp.getNumTiles(); 254 int nComp = wp.getNumComponents(); 255 int numLayers = lyrs.getTotNumLayers(); 256 257 // Parse the Progression type 258 wp.setProgressionType(lyrs, wp.getProgressionName()); 259 260 return new EBCOTRateAllocator(src,lyrs,bw,wp); 261 } 262 263 /** 264 * Convenience method that parses the 'Alayers' option. 265 * 266 * @param params The parameters of the 'Alayers' option 267 * 268 * @param rate The overall target bitrate 269 * 270 * @return The layer specification. 271 * */ 272 private static LayersInfo parseAlayers(String params, float rate) { 273 LayersInfo lyrs; 274 StreamTokenizer stok; 275 boolean islayer,ratepending; 276 float r; 277 278 lyrs = new LayersInfo(rate); 279 stok = new StreamTokenizer(new StringReader(params)); 280 stok.eolIsSignificant(false); 281 282 try { 283 stok.nextToken(); 284 } 285 catch (IOException e) { 286 throw new Error("An IOException has ocurred where it "+ 287 "should never occur"); 288 } 289 ratepending = false; 290 islayer = false; 291 r = 0; // to keep compiler happy 292 while (stok.ttype != stok.TT_EOF) { 293 switch(stok.ttype) { 294 case StreamTokenizer.TT_NUMBER: 295 if (islayer) { // layer parameter 296 try { 297 lyrs.addOptPoint(r,(int)stok.nval); 298 } 299 catch (IllegalArgumentException e) { 300 throw new 301 IllegalArgumentException("Error in 'Alayers' "+ 302 "option: "+e.getMessage()); 303 } 304 ratepending = false; 305 islayer = false; 306 } 307 else { // rate parameter 308 if (ratepending) { // Add pending rate parameter 309 try { 310 lyrs.addOptPoint(r,0); 311 } 312 catch (IllegalArgumentException e) { 313 throw new 314 IllegalArgumentException("Error in 'Alayers' "+ 315 "option: "+ 316 e.getMessage()); 317 } 318 } 319 // Now store new rate parameter 320 r = (float) stok.nval; 321 ratepending = true; 322 } 323 break; 324 case '+': 325 if (!ratepending || islayer) { 326 throw new 327 IllegalArgumentException("Layer parameter without "+ 328 "previous rate parameter "+ 329 "in 'Alayers' option"); 330 } 331 islayer = true; // Next number is layer parameter 332 break; 333 case StreamTokenizer.TT_WORD: 334 try { 335 stok.nextToken(); 336 } catch(IOException e) { 337 throw new Error("An IOException has ocurred where it "+ 338 "should never occur"); 339 } 340 if (stok.ttype != stok.TT_EOF) { 341 throw new 342 IllegalArgumentException("'sl' argument of "+ 343 "'-Alayers' option must be "+ 344 "used alone."); 345 } 346 break; 347 default: 348 throw new IllegalArgumentException("Error parsing 'Alayers' "+ 349 "option"); 350 } 351 try { 352 stok.nextToken(); 353 } 354 catch (IOException e) { 355 throw new Error("An IOException has ocurred where it "+ 356 "should never occur"); 357 } 358 } 359 if (islayer) { 360 throw new IllegalArgumentException("Error parsing 'Alayers' "+ 361 "option"); 362 } 363 if (ratepending) { 364 try { 365 lyrs.addOptPoint(r,0); 366 } 367 catch (IllegalArgumentException e) { 368 throw new 369 IllegalArgumentException("Error in 'Alayers' "+ 370 "option: "+ 371 e.getMessage()); 372 } 373 } 374 return lyrs; 375 } 376 377}