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 jj2000.j2k.codestream.writer.*;
048import jj2000.j2k.wavelet.analysis.*;
049import jj2000.j2k.codestream.*;
050import jj2000.j2k.entropy.*;
051import jj2000.j2k.image.*;
052import jj2000.j2k.util.*;
053import jj2000.j2k.*;
054
055import java.io.*;
056
057import com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageWriteParamJava;
058/**
059 * This is the abstract class from which post-compression rate allocators
060 * which generate layers should inherit. The source of data is a
061 * 'CodedCBlkDataSrcEnc' which delivers entropy coded blocks with
062 * rate-distortion statistics.
063 *
064 * <P>The post compression rate allocator implementation should create the
065 * layers, according to a rate allocation policy, and send the packets to a
066 * CodestreamWriter. Since the rate allocator sends the packets to the bit
067 * stream then it should output the packets to the bit stream in the order
068 * imposed by the bit stream profiles.
069 *
070 * @see CodedCBlkDataSrcEnc
071 *
072 * @see jj2000.j2k.codestream.writer.CodestreamWriter
073 * */
074public abstract class PostCompRateAllocator extends ImgDataAdapter {
075
076    /** The prefix for rate allocation options: 'A' */
077    public final static char OPT_PREFIX = 'A';
078
079    /** The list of parameters that is accepted for entropy coding. Options
080     * for entropy coding start with 'R'. */
081    private final static String [][] pinfo = {
082        { "Aptype", "[<tile idx>] res|layer|res-pos|"+
083          "pos-comp|comp-pos [res_start comp_start layer_end res_end "+
084          "comp_end "+
085          "prog] [[res_start comp_start ly_end res_end comp_end prog] ...] ["+
086          "[<tile-component idx>] ...]",
087          "Specifies which type of progression should be used when "+
088          "generating "+
089          "the codestream. The 'res' value generates a resolution "+
090          "progressive codestream with the number of layers specified by "+
091          "'Alayers' option. The 'layer' value generates a layer progressive "+
092          "codestream with multiple layers. In any case the rate-allocation "+
093          "algorithm optimizes for best quality in each layer. The quality "+
094          "measure is mean squared error (MSE) or a weighted version of it "+
095          "(WMSE). If no progression type is specified or imposed by other "+
096          "modules, the default value is 'layer'.\n"+
097          "It is also possible to describe progression order changes. In "+
098          "this case, 'res_start' is the index (from 0) of the first "+
099          "resolution "+
100          "level, 'comp_start' is the index (from 0) of the first component, "+
101          "'ly_end' is the index (from 0) of the first layer not included, "+
102          "'res_end' is the index (from 0) of the first resolution level not "+
103          "included, 'comp_end' is index (from 0) of the first component not "+
104          "included and 'prog' is the progression type to be used "+
105          "for the rest of the tile/image. Several progression order changes "+
106          "can be specified, one after the other."
107          , null},
108        { "Alayers", "<rate> [+<layers>] [<rate [+<layers>] [...]]",
109          "Explicitly specifies the codestream layer formation parameters. "+
110          "The <rate> parameter specifies the bitrate to which the first "+
111          "layer should be optimized. The <layers> parameter, if present, "+
112          "specifies the number of extra layers that should be added for "+
113          "scalability. These extra layers are not optimized. "+
114          "Any extra <rate> and <layers> parameters add more layers, in the "+
115          "same way. An additional layer is always added at the end, which"+
116          " is "+
117          "optimized to the overall target bitrate of the bit stream. Any "+
118          "layers (optimized or not) whose target bitrate is higher that the "+
119          "overall target bitrate are silently ignored. The bitrates of the "+
120          "extra layers that are added through the <layers> parameter are "+
121          "approximately log-spaced between the other target bitrates. If "+
122          "several <rate> [+<layers>] constructs appear the <rate>"+
123          " parameters "+
124          "must appear in increasing order. The rate allocation algorithm "+
125          "ensures that all coded layers have a minimal reasonable size, if "+
126          "not these layers are silently ignored.","0.015 +20 2.0 +10"}
127    };
128
129    /** The source of entropy coded data */
130    protected CodedCBlkDataSrcEnc src;
131
132    /** The source of entropy coded data */
133    protected J2KImageWriteParamJava wp;
134
135    /** The number of layers. */
136    protected int numLayers;
137
138    /** The bit-stream writer */
139    CodestreamWriter bsWriter;
140
141    /** The header encoder */
142    HeaderEncoder headEnc;
143
144    /**
145     * Initializes the source of entropy coded data.
146     *
147     * @param src The source of entropy coded data.
148     *
149     * @param ln The number of layers to create
150     *
151     * @param pt The Progression type, as defined in 'ProgressionType'.
152     *
153     * @param bw The packet bit stream writer.
154     *
155     * @see ProgressionType
156     * */
157    public PostCompRateAllocator(CodedCBlkDataSrcEnc src, int nl,
158                                 CodestreamWriter bw, J2KImageWriteParamJava wp) {
159        super(src);
160        this.src = src;
161        this.wp = wp;
162        numLayers = nl;
163        bsWriter = bw;
164    }
165
166    /**
167     * Keep a reference to the header encoder.
168     *
169     * @param headEnc The header encoder
170     * */
171    public void setHeaderEncoder(HeaderEncoder headEnc){
172        this.headEnc = headEnc;
173    }
174
175    /**
176     * Initializes the rate allocation points, taking into account header
177     * overhead and such. This method must be called after the header has been
178     * simulated but before calling the runAndWrite() one. The header must be
179     * rewritten after a call to this method since the number of layers may
180     * change.
181     *
182     * @param oldSyntax Whether or not the old syntax is used.
183     *
184     * @see #runAndWrite
185     * */
186    public abstract void initialize() throws IOException;
187
188    /**
189     * Runs the rate allocation algorithm and writes the data to the
190     * bit stream. This must be called after the initialize() method.
191     *
192     * @see #initialize
193     * */
194    public abstract void runAndWrite() throws IOException;
195
196    /**
197     * Returns the number of layers that are actually generated.
198     *
199     * @return The number of layers generated.
200     * */
201    public int getNumLayers() {
202        return numLayers;
203    }
204
205    /**
206     * Returns the parameters that are used in this class and implementing
207     * classes. It returns a 2D String array. Each of the 1D arrays is for a
208     * different option, and they have 3 elements. The first element is the
209     * option name, the second one is the synopsis, the third one is a long
210     * description of what the parameter is and the fourth is its default
211     * value. The synopsis or description may be 'null', in which case it is
212     * assumed that there is no synopsis or description of the option,
213     * respectively. Null may be returned if no options are supported.
214     *
215     * @return the options name, their synopsis and their explanation,
216     * or null if no options are supported.
217     * */
218    public static String[][] getParameterInfo() {
219        return pinfo;
220    }
221
222    /**
223     * Creates a PostCompRateAllocator object for the appropriate rate
224     * allocation parameters in the parameter list 'pl', having 'src' as the
225     * source of entropy coded data, 'rate' as the target bitrate and 'bw' as
226     * the bit stream writer object.
227     *
228     * @param src The source of entropy coded data.
229     *
230     * @param pl The parameter lis (or options).
231     *
232     * @param rate The target bitrate for the rate allocation
233     *
234     * @param bw The bit stream writer object, where the bit stream data will
235     * be written.
236     * */
237    public static PostCompRateAllocator createInstance(CodedCBlkDataSrcEnc src,
238                                                       float rate,
239                                                       CodestreamWriter bw,
240                                                       J2KImageWriteParamJava wp){
241        String lyropt = wp.getLayers();
242        if (lyropt == null) {
243            if(wp.getROIs().getSpecified() == null) {
244                lyropt = "res";
245            }
246            else {
247                lyropt = "layer";
248            }
249        }
250
251        // Construct the layer specification from the Alayers option
252        LayersInfo lyrs = parseAlayers(lyropt,rate);
253
254        int nTiles = wp.getNumTiles();
255        int nComp = wp.getNumComponents();
256        int numLayers = lyrs.getTotNumLayers();
257
258        // Parse the Progression type
259        wp.setProgressionType(lyrs, wp.getProgressionName());
260
261        return new EBCOTRateAllocator(src,lyrs,bw,wp);
262    }
263
264    /**
265     * Convenience method that parses the 'Alayers' option.
266     *
267     * @param params The parameters of the 'Alayers' option
268     *
269     * @param rate The overall target bitrate
270     *
271     * @return The layer specification.
272     * */
273    private static LayersInfo parseAlayers(String params, float rate) {
274        LayersInfo lyrs;
275        StreamTokenizer stok;
276        boolean islayer,ratepending;
277        float r;
278
279        lyrs = new LayersInfo(rate);
280        stok = new StreamTokenizer(new StringReader(params));
281        stok.eolIsSignificant(false);
282
283        try {
284            stok.nextToken();
285        }
286        catch (IOException e) {
287            throw new Error("An IOException has ocurred where it "+
288                            "should never occur");
289        }
290        ratepending = false;
291        islayer = false;
292        r = 0; // to keep compiler happy
293        while (stok.ttype != stok.TT_EOF) {
294            switch(stok.ttype) {
295            case StreamTokenizer.TT_NUMBER:
296                if (islayer) { // layer parameter
297                    try {
298                        lyrs.addOptPoint(r,(int)stok.nval);
299                    }
300                    catch (IllegalArgumentException e) {
301                        throw new
302                            IllegalArgumentException("Error in 'Alayers' "+
303                                                     "option: "+e.getMessage());
304                    }
305                    ratepending = false;
306                    islayer = false;
307                }
308                else { // rate parameter
309                    if (ratepending) { // Add pending rate parameter
310                        try {
311                            lyrs.addOptPoint(r,0);
312                        }
313                        catch (IllegalArgumentException e) {
314                            throw new
315                                IllegalArgumentException("Error in 'Alayers' "+
316                                                         "option: "+
317                                                         e.getMessage());
318                        }
319                    }
320                    // Now store new rate parameter
321                    r = (float) stok.nval;
322                    ratepending = true;
323                }
324                break;
325            case '+':
326                if (!ratepending || islayer) {
327                    throw new
328                        IllegalArgumentException("Layer parameter without "+
329                                                 "previous rate parameter "+
330                                                 "in 'Alayers' option");
331                }
332                islayer = true; // Next number is layer parameter
333                break;
334            case StreamTokenizer.TT_WORD:
335                try {
336                    stok.nextToken();
337                } catch(IOException e) {
338                    throw new Error("An IOException has ocurred where it "+
339                                    "should never occur");
340                }
341                if (stok.ttype != stok.TT_EOF) {
342                    throw new
343                        IllegalArgumentException("'sl' argument of "+
344                                                 "'-Alayers' option must be "+
345                                                 "used alone.");
346                }
347                break;
348            default:
349                throw new IllegalArgumentException("Error parsing 'Alayers' "+
350                                                   "option");
351            }
352            try {
353                stok.nextToken();
354            }
355            catch (IOException e) {
356                throw new Error("An IOException has ocurred where it "+
357                                "should never occur");
358            }
359        }
360        if (islayer) {
361            throw new IllegalArgumentException("Error parsing 'Alayers' "+
362                                               "option");
363        }
364        if (ratepending) {
365            try {
366                lyrs.addOptPoint(r,0);
367            }
368            catch (IllegalArgumentException e) {
369                throw new
370                    IllegalArgumentException("Error in 'Alayers' "+
371                                             "option: "+
372                                             e.getMessage());
373            }
374        }
375        return lyrs;
376    }
377
378}