001/*
002 * $RCSfile: AnWTFilterSpec.java,v $
003 * $Revision: 1.1 $
004 * $Date: 2005/02/11 05:02:29 $
005 * $State: Exp $
006 *
007 * Class:                   AnWTFilterSpec
008 *
009 * Description:             Analysis filters specification
010 *
011 *
012 *
013 * COPYRIGHT:
014 *
015 * This software module was originally developed by Raphaël Grosbois and
016 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
017 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
018 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
019 * Centre France S.A) in the course of development of the JPEG2000
020 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
021 * software module is an implementation of a part of the JPEG 2000
022 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
023 * Systems AB and Canon Research Centre France S.A (collectively JJ2000
024 * Partners) agree not to assert against ISO/IEC and users of the JPEG
025 * 2000 Standard (Users) any of their rights under the copyright, not
026 * including other intellectual property rights, for this software module
027 * with respect to the usage by ISO/IEC and Users of this software module
028 * or modifications thereof for use in hardware or software products
029 * claiming conformance to the JPEG 2000 Standard. Those intending to use
030 * this software module in hardware or software products are advised that
031 * their use may infringe existing patents. The original developers of
032 * this software module, JJ2000 Partners and ISO/IEC assume no liability
033 * for use of this software module or modifications thereof. No license
034 * or right to this software module is granted for non JPEG 2000 Standard
035 * conforming products. JJ2000 Partners have full right to use this
036 * software module for his/her own purpose, assign or donate this
037 * software module to any third party and to inhibit third parties from
038 * using this software module for non JPEG 2000 Standard conforming
039 * products. This copyright notice must be included in all copies or
040 * derivative works of this software module.
041 *
042 * Copyright (c) 1999/2000 JJ2000 Partners.
043 * */
044package jj2000.j2k.wavelet.analysis;
045
046import java.util.StringTokenizer;
047
048import jj2000.j2k.ModuleSpec;
049import jj2000.j2k.quantization.QuantTypeSpec;
050
051import com.github.jaiimageio.jpeg2000.impl.J2KImageWriteParamJava;
052
053/**
054 * This class extends ModuleSpec class for analysis filters specification
055 * holding purpose.
056 *
057 * @see ModuleSpec
058 * */
059public class AnWTFilterSpec extends ModuleSpec {
060
061    /** The reversible default filter */
062    private final static String REV_FILTER_STR = "w5x3";
063
064    /** The non-reversible default filter */
065    private final static String NON_REV_FILTER_STR = "w9x7";
066
067    /**
068     * Constructs a new 'AnWTFilterSpec' for the specified number of
069     * components and tiles.
070     *
071     * @param nt The number of tiles
072     *
073     * @param nc The number of components
074     *
075     * @param type the type of the specification module i.e. tile specific,
076     * component specific or both.
077     *
078     * @param qts Quantization specifications
079     * */
080    public AnWTFilterSpec(int nt, int nc, byte type,
081                          QuantTypeSpec qts, J2KImageWriteParamJava wp, String values){
082        super(nt, nc, type);
083/*
084        // Check parameters
085        pl.checkList(AnWTFilter.OPT_PREFIX,
086                     pl.toNameArray(AnWTFilter.getParameterInfo()));
087*/
088        specified = values;
089        String param = specified;
090        boolean isFilterSpecified = true;
091
092        // No parameter specified
093        if(values==null){
094            isFilterSpecified = false;
095
096            if(wp.getLossless()) {
097                setDefault(parseFilters(REV_FILTER_STR));
098                return;
099            }
100
101            // If no filter is specified through the command-line, use
102            // REV_FILTER_STR or NON_REV_FILTER_STR according to the
103            // quantization type
104            for(int t=nt-1;t>=0;t--){
105                for(int c=nc-1;c>=0;c--){
106                    switch(qts.getSpecValType(t,c)){
107                    case SPEC_DEF:
108                        if(getDefault()==null){
109                            if( wp.getLossless() )
110                                setDefault(parseFilters(REV_FILTER_STR));
111                            if( ((String)qts.getDefault()).
112                                equals("reversible") ){
113                                setDefault(parseFilters(REV_FILTER_STR));
114                            }
115                            else{
116                                setDefault(parseFilters(NON_REV_FILTER_STR));
117                            }
118                        }
119                        specValType[t][c] = SPEC_DEF;
120                        break;
121                    case SPEC_COMP_DEF:
122                        if(!isCompSpecified(c)){
123                            if( ((String)qts.getCompDef(c)).
124                                equals("reversible") ){
125                                setCompDef(c,parseFilters(REV_FILTER_STR));
126                            }
127                            else{
128                                setCompDef(c,parseFilters(NON_REV_FILTER_STR));
129                            }
130                        }
131                        specValType[t][c] = SPEC_COMP_DEF;
132                        break;
133                    case SPEC_TILE_DEF:
134                        if(!isTileSpecified(t)){
135                            if( ((String)qts.getTileDef(t)).
136                                equals("reversible") ){
137                                setTileDef(t,parseFilters(REV_FILTER_STR));
138                            }
139                            else{
140                                setTileDef(t,parseFilters(NON_REV_FILTER_STR));
141                            }
142                        }
143                        specValType[t][c] = SPEC_TILE_DEF;
144                        break;
145                    case SPEC_TILE_COMP:
146                        if(!isTileCompSpecified(t,c)){
147                            if(((String)qts.getTileCompVal(t,c)).
148                               equals("reversible")){
149                                setTileCompVal(t,c,parseFilters(REV_FILTER_STR));
150                            }
151                            else{
152                                setTileCompVal(t,c,
153                                               parseFilters(NON_REV_FILTER_STR));
154                            }
155                        }
156                        specValType[t][c] = SPEC_TILE_COMP;
157                        break;
158                    default:
159                        throw new IllegalArgumentException("Unsupported "+
160                                                           "specification type");
161                    }
162                }
163            }
164
165            return;
166        }
167
168        // Parse argument
169        StringTokenizer stk = new StringTokenizer(param);
170        String word; // current word
171        byte curSpecType = SPEC_DEF; // Specification type of the
172        // current parameter
173        boolean[] tileSpec = null; // Tiles concerned by the specification
174        boolean[] compSpec = null; // Components concerned by the specification
175        AnWTFilter[][] filter;
176
177        while(stk.hasMoreTokens()){
178            word = stk.nextToken();
179
180            switch(word.charAt(0)){
181            case 't': // Tiles specification
182            case 'T': // Tiles specification
183                tileSpec = parseIdx(word,nTiles);
184                if(curSpecType==SPEC_COMP_DEF)
185                    curSpecType = SPEC_TILE_COMP;
186                else
187                    curSpecType = SPEC_TILE_DEF;
188                break;
189            case 'c': // Components specification
190            case 'C': // Components specification
191                compSpec = parseIdx(word,nComp);
192                if(curSpecType==SPEC_TILE_DEF)
193                    curSpecType = SPEC_TILE_COMP;
194                else
195                    curSpecType = SPEC_COMP_DEF;
196                break;
197            case 'w': // WT filters specification
198            case 'W': // WT filters specification
199                if(wp.getLossless() &&
200                   word.equalsIgnoreCase("w9x7") )  {
201                    throw new IllegalArgumentException("Cannot use non "+
202                                                       "reversible "+
203                                                       "wavelet transform with"+
204                                                       " '-lossless' option");
205
206                }
207
208                filter = parseFilters(word);
209                if(curSpecType==SPEC_DEF){
210                    setDefault(filter);
211                }
212                else if(curSpecType==SPEC_TILE_DEF){
213                    for(int i=tileSpec.length-1; i>=0; i--)
214                        if(tileSpec[i]){
215                            setTileDef(i,filter);
216                        }
217                }
218                else if(curSpecType==SPEC_COMP_DEF){
219                    for(int i=compSpec.length-1; i>=0; i--)
220                        if(compSpec[i]){
221                            setCompDef(i,filter);
222                        }
223                }
224                else{
225                    for(int i=tileSpec.length-1; i>=0; i--){
226                        for(int j=compSpec.length-1; j>=0 ; j--){
227                            if(tileSpec[i] && compSpec[j]){
228                                setTileCompVal(i,j,filter);
229                            }
230                        }
231                    }
232                }
233
234                // Re-initialize
235                curSpecType = SPEC_DEF;
236                tileSpec = null;
237                compSpec = null;
238                break;
239
240            default:
241                throw new IllegalArgumentException("Bad construction for "+
242                                                   "parameter: "+word);
243            }
244        }
245
246        // Check that default value has been specified
247        if(getDefault()==null){
248            int ndefspec = 0;
249            for(int t=nt-1; t>=0; t--){
250                for(int c=nc-1; c>=0 ; c--){
251                    if(specValType[t][c] == SPEC_DEF){
252                        ndefspec++;
253                    }
254                }
255            }
256
257            // If some tile-component have received no specification, it takes
258            // the default value
259            if(ndefspec!=0){
260                if( ((String)qts.getDefault()).equals("reversible") )
261                    setDefault(parseFilters(REV_FILTER_STR));
262                else
263                    setDefault(parseFilters(NON_REV_FILTER_STR));
264            }
265            else{
266                // All tile-component have been specified, takes the first
267                // tile-component value as default.
268                setDefault(getTileCompVal(0,0));
269                switch(specValType[0][0]){
270                case SPEC_TILE_DEF:
271                    for(int c=nc-1; c>=0; c--){
272                        if(specValType[0][c]==SPEC_TILE_DEF)
273                            specValType[0][c] = SPEC_DEF;
274                    }
275                    tileDef[0] = null;
276                    break;
277                case SPEC_COMP_DEF:
278                    for(int t=nt-1; t>=0; t--){
279                        if(specValType[t][0]==SPEC_COMP_DEF)
280                            specValType[t][0] = SPEC_DEF;
281                    }
282                    compDef[0] = null;
283                    break;
284                case SPEC_TILE_COMP:
285                    specValType[0][0] = SPEC_DEF;
286                    tileCompVal.put("t0c0",null);
287                    break;
288                }
289            }
290        }
291
292        // Check consistency between filter and quantization type
293        // specification
294        for(int t=nt-1;t>=0;t--){
295            for(int c=nc-1;c>=0;c--){
296                // Reversible quantization
297                if( ((String)qts.getTileCompVal(t,c)).equals("reversible")){
298                    // If filter is reversible, it is OK
299                    if(isReversible(t,c)) continue;
300
301                    // If no filter has been defined, use reversible filter
302                    if(!isFilterSpecified){
303                        setTileCompVal(t,c,parseFilters(REV_FILTER_STR));
304                    }
305                    else{
306                        // Non reversible filter specified -> Error
307                        throw new IllegalArgumentException("Filter of "+
308                                                           "tile-component"+
309                                                           " ("+t+","+c+") does"+
310                                                           " not allow "+
311                                                           "reversible "+
312                                                           "quantization. "+
313                                                           "Specify '-Qtype "+
314                                                           "expounded' or "+
315                                                           "'-Qtype derived'"+
316                                                           "in "+
317                                                           "the command line.");
318                    }
319                }
320                else{ // No reversible quantization
321                    // No reversible filter -> OK
322                    if(!isReversible(t,c)) continue;
323
324                    // If no filter has been specified, use non-reversible
325                    // filter
326                    if(!isFilterSpecified){
327                        setTileCompVal(t,c,parseFilters(NON_REV_FILTER_STR));
328                    }
329                    else{
330                        // Reversible filter specified -> Error
331                        throw new IllegalArgumentException("Filter of "+
332                                                           "tile-component"+
333                                                           " ("+t+","+c+") does"+
334                                                           " not allow "+
335                                                           "non-reversible "+
336                                                           "quantization. "+
337                                                           "Specify '-Qtype "+
338                                                           "reversible' in "+
339                                                           "the command line");
340                    }
341                }
342            }
343        }
344    }
345
346    /**
347     * Parse filters from the given word
348     *
349     * @param word String to parse
350     *
351     * @return Analysis wavelet filter (first dimension: by direction,
352     * second dimension: by decomposition levels)
353     */
354    private AnWTFilter[][] parseFilters(String word){
355        AnWTFilter[][] filt=new AnWTFilter[2][1];
356        if(word.equalsIgnoreCase("w5x3")){
357            filt[0][0]=new AnWTFilterIntLift5x3();
358            filt[1][0]=new AnWTFilterIntLift5x3();
359            return filt;
360        }
361        else if(word.equalsIgnoreCase("w9x7")){
362            filt[0][0]=new AnWTFilterFloatLift9x7();
363            filt[1][0]=new AnWTFilterFloatLift9x7();
364            return filt;
365        }
366        else{
367            throw new
368                IllegalArgumentException("Non JPEG 2000 part I filter: "
369                                         +word);
370        }
371    }
372
373    /**
374     * Returns the data type used by the filters in this object, as defined in
375     * the 'DataBlk' interface for specified tile-component.
376     *
377     * @param t Tile index
378     *
379     * @param c Component index
380     *
381     * @return The data type of the filters in this object
382     *
383     * @see jj2000.j2k.image.DataBlk
384     * */
385    public int getWTDataType(int t,int c){
386        AnWTFilter[][] an = (AnWTFilter[][])getSpec(t,c);
387        return an[0][0].getDataType();
388    }
389
390    /**
391     * Returns the horizontal analysis filters to be used in component 'n' and
392     * tile 't'.
393     *
394     * <P>The horizontal analysis filters are returned in an array of
395     * AnWTFilter. Each element contains the horizontal filter for each
396     * resolution level starting with resolution level 1 (i.e. the analysis
397     * filter to go from resolution level 1 to resolution level 0). If there
398     * are less elements than the maximum resolution level, then the last
399     * element is assumed to be repeated.
400     *
401     * @param t The tile index, in raster scan order
402     *
403     * @param c The component index.
404     *
405     * @return The array of horizontal analysis filters for component 'n' and
406     * tile 't'.
407     * */
408    public AnWTFilter[] getHFilters(int t, int c) {
409        AnWTFilter[][] an = (AnWTFilter[][])getSpec(t,c);
410        return an[0];
411    }
412
413    /**
414     * Returns the vertical analysis filters to be used in component 'n' and
415     * tile 't'.
416     *
417     * <P>The vertical analysis filters are returned in an array of
418     * AnWTFilter. Each element contains the vertical filter for each
419     * resolution level starting with resolution level 1 (i.e. the analysis
420     * filter to go from resolution level 1 to resolution level 0). If there
421     * are less elements than the maximum resolution level, then the last
422     * element is assumed to be repeated.
423     *
424     * @param t The tile index, in raster scan order
425     *
426     * @param c The component index.
427     *
428     * @return The array of horizontal analysis filters for component 'n' and
429     * tile 't'.
430     * */
431    public AnWTFilter[] getVFilters(int t,int c) {
432        AnWTFilter[][] an = (AnWTFilter[][])getSpec(t,c);
433        return an[1];
434    }
435
436    /** Debugging method */
437    public String toString(){
438        String str = "";
439        AnWTFilter[][] an;
440
441        str += "nTiles="+nTiles+"\nnComp="+nComp+"\n\n";
442
443        for(int t=0; t<nTiles; t++){
444            for(int c=0; c<nComp; c++){
445                an = (AnWTFilter[][])getSpec(t,c);
446
447                str += "(t:"+t+",c:"+c+")\n";
448
449                // Horizontal filters
450                str += "\tH:";
451                for(int i=0; i<an[0].length; i++)
452                    str += " "+an[0][i];
453                // Horizontal filters
454                str += "\n\tV:";
455                for(int i=0; i<an[1].length; i++)
456                    str += " "+an[1][i];
457                str += "\n";
458            }
459        }
460
461        return str;
462    }
463
464    /**
465     * Check the reversibility of filters contained is the given
466     * tile-component.
467     *
468     * @param t The index of the tile
469     *
470     * @param c The index of the component
471     * */
472    public boolean isReversible(int t,int c){
473        // Note: no need to buffer the result since this method is
474        // normally called once per tile-component.
475        AnWTFilter[]
476            hfilter = getHFilters(t,c),
477            vfilter = getVFilters(t,c);
478
479        // As soon as a filter is not reversible, false can be returned
480        for(int i=hfilter.length-1; i>=0; i--)
481            if(!hfilter[i].isReversible() || !vfilter[i].isReversible())
482                return false;
483        return true;
484    }
485}