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}