001: /*
002: * $RCSfile: SubbandAn.java,v $
003: * $Revision: 1.1 $
004: * $Date: 2005/02/11 05:02:31 $
005: * $State: Exp $
006: *
007: * Class: SubbandAn
008: *
009: * Description: Element for a tree structure for a descripotion
010: * of subbands on the anslysis side.
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: *
045: *
046: *
047: */
048:
049: package jj2000.j2k.wavelet.analysis;
050:
051: import jj2000.j2k.wavelet.*;
052:
053: /**
054: * This class represents a subband in a bidirectional tree structure
055: * that describes the subband decomposition for a wavelet transform,
056: * specifically for the analysis side.
057: *
058: * <P>The element can be either a node or a leaf of the tree. If it is
059: * a node then ther are 4 descendants (LL, HL, LH and HH). If it is a
060: * leaf there are no descendants.
061: *
062: * <P>The tree is bidirectional. Each element in the tree structure
063: * has a "parent", which is the subband from which the element was
064: * obtained by decomposition. The only exception is the root element
065: * which has no parent (i.e.it's null), for obvious reasons.
066: * */
067: public class SubbandAn extends Subband {
068:
069: /**
070: * The reference to the parent of this subband. It is null for the
071: * root element. It is null by default. */
072: public SubbandAn parent = null;
073:
074: /**
075: * The reference to the LL subband resulting from the
076: * decomposition of this subband. It is null by default. */
077: public SubbandAn subb_LL;
078:
079: /**
080: * The reference to the HL subband (horizontal high-pass)
081: * resulting from the decomposition of this subband. It is null by
082: * default. */
083: public SubbandAn subb_HL;
084:
085: /**
086: * The reference to the LH subband (vertical high-pass) resulting
087: * from the decomposition of this subband. It is null by default.
088: * */
089: public SubbandAn subb_LH;
090:
091: /**
092: * The reference to the HH subband resulting from the
093: * decomposition of this subband. It is null by default.
094: */
095: public SubbandAn subb_HH;
096:
097: /** The horizontal analysis filter used to decompose this
098: subband. This is applicable to "node" elements only. The
099: default value is null. */
100: public AnWTFilter hFilter;
101:
102: /** The vertical analysis filter used to decompose this
103: subband. This is applicable to "node" elements only. The
104: default value is null. */
105: public AnWTFilter vFilter;
106:
107: /**
108: * The L2-norm of the synthesis basis waveform of this subband,
109: * applicable to "leafs" only. By default it is -1 (i.e. not
110: * calculated yet).
111: * */
112: public float l2Norm = -1.0f;
113:
114: /**
115: * The contribution to the MSE or WMSE error that would result in the
116: * image if there was an error of exactly one quantization step size in
117: * the sample of the subband. This value is expressed relative to a
118: * nominal dynamic range in the image domain of exactly 1.0. This field
119: * contains valid data only after quantization 9See Quantizer).
120: *
121: * @see jj2000.j2k.quantization.quantizer.Quantizer
122: * */
123: public float stepWMSE;
124:
125: /**
126: * Creates a SubbandAn element with all the default values. The
127: * dimensions are (0,0) and the upper left corner is (0,0).
128: *
129: *
130: * */
131: public SubbandAn() {
132: }
133:
134: /**
135: * Creates the top-level node and the entire subband tree, with
136: * the top-level dimensions, the number of decompositions, and the
137: * decomposition tree as specified.
138: *
139: * <P>This constructor just calls the same constructor of the
140: * super class, and then calculates the L2-norm (or energy weight)
141: * of each leaf.
142: *
143: * <P>This constructor does not initialize the value of the magBits or
144: * stepWMSE member variables. This variables are normally initialized by
145: * the quantizer (see Quantizer).
146: *
147: * @param w The top-level width
148: *
149: * @param h The top-level height
150: *
151: * @param ulcx The horizontal coordinate of the upper-left corner with
152: * respect to the canvas origin, in the component grid.
153: *
154: * @param ulcy The vertical coordinate of the upper-left corner with
155: * respect to the canvas origin, in the component grid.
156: *
157: * @param lvls The number of levels (or LL decompositions) in the
158: * tree.
159: *
160: * @param hfilters The horizontal wavelet analysis filters for each
161: * resolution level, starting at resolution level 0.
162: *
163: * @param vfilters The vertical wavelet analysis filters for each
164: * resolution level, starting at resolution level 0.
165: *
166: * @see Subband#Subband(int,int,int,int,int,
167: * WaveletFilter[],WaveletFilter[])
168: *
169: * @see jj2000.j2k.quantization.quantizer.Quantizer
170: *
171: *
172: * */
173: public SubbandAn(int w, int h, int ulcx, int ulcy, int lvls,
174: WaveletFilter hfilters[], WaveletFilter vfilters[]) {
175: super (w, h, ulcx, ulcy, lvls, hfilters, vfilters);
176: // Caculate the L2-norms
177: calcL2Norms();
178: }
179:
180: /**
181: * Returns the parent of this subband. The parent of a subband is
182: * the subband from which this one was obtained by
183: * decomposition. The root element has no parent subband (null).
184: *
185: * @return The parent subband, or null for the root one.
186: *
187: *
188: * */
189: public Subband getParent() {
190: return parent;
191: }
192:
193: /**
194: * Returns the LL child subband of this subband.
195: *
196: * @return The LL child subband, or null if there are no childs.
197: *
198: *
199: * */
200: public Subband getLL() {
201: return subb_LL;
202: }
203:
204: /**
205: * Returns the HL (horizontal high-pass) child subband of this
206: * subband.
207: *
208: * @return The HL child subband, or null if there are no childs.
209: *
210: *
211: * */
212: public Subband getHL() {
213: return subb_HL;
214: }
215:
216: /**
217: * Returns the LH (vertical high-pass) child subband of this
218: * subband.
219: *
220: * @return The LH child subband, or null if there are no childs.
221: *
222: *
223: * */
224: public Subband getLH() {
225: return subb_LH;
226: }
227:
228: /**
229: * Returns the HH child subband of this subband.
230: *
231: * @return The HH child subband, or null if there are no childs.
232: *
233: *
234: * */
235: public Subband getHH() {
236: return subb_HH;
237: }
238:
239: /**
240: * Splits the current subband in its four subbands. It changes the
241: * status of this element (from a leaf to a node, and sets the
242: * filters), creates the childs and initializes them. An
243: * IllegalArgumentException is thrown if this subband is not a
244: * leaf.
245: *
246: * <P>It uses the initChilds() method to initialize the childs.
247: *
248: * @param hfilter The horizontal wavelet filter used to decompose
249: * this subband. It has to be a AnWTFilter object.
250: *
251: * @param vfilter The vertical wavelet filter used to decompose this
252: * subband. It has to be a AnWTFilter object.
253: *
254: * @return A reference to the LL leaf (subb_LL).
255: *
256: * @see Subband#initChilds
257: *
258: *
259: * */
260: protected Subband split(WaveletFilter hfilter, WaveletFilter vfilter) {
261: // Test that this is a node
262: if (isNode) {
263: throw new IllegalArgumentException();
264: }
265:
266: // Modify this element into a node and set the filters
267: isNode = true;
268: this .hFilter = (AnWTFilter) hfilter;
269: this .vFilter = (AnWTFilter) vfilter;
270:
271: // Create childs
272: subb_LL = new SubbandAn();
273: subb_LH = new SubbandAn();
274: subb_HL = new SubbandAn();
275: subb_HH = new SubbandAn();
276:
277: // Assign parent
278: subb_LL.parent = this ;
279: subb_HL.parent = this ;
280: subb_LH.parent = this ;
281: subb_HH.parent = this ;
282:
283: // Initialize childs
284: initChilds();
285:
286: // Return reference to LL subband
287: return subb_LL;
288: }
289:
290: /**
291: * Calculates the basis waveform of the first leaf for which the
292: * L2-norm has not been calculated yet. This method searches
293: * recursively for the first leaf for which the value has not been
294: * calculated yet, and then calculates the L2-norm on the return
295: * path.
296: *
297: * <P>The wfs argument should be a size 2 array of float arrays
298: * (i.e. 2D array) and it must be of length 2 (or more). When
299: * returning, wfs[0] will contain the line waveform, and wfs[1]
300: * will contain the column waveform.
301: *
302: * <P>This method can not be called on an element that ahs a
303: * non-negative value in l2Norm, since that means that we are
304: * done.
305: *
306: * @param wfs An size 2 array where the line and column waveforms
307: * will be returned.
308: *
309: *
310: * */
311: private void calcBasisWaveForms(float wfs[][]) {
312: if (l2Norm < 0) {
313: // We are not finished with this element yet
314: if (isNode) {
315: // We are on a node => search on childs
316: if (subb_LL.l2Norm < 0f) {
317: subb_LL.calcBasisWaveForms(wfs);
318: wfs[0] = hFilter.getLPSynWaveForm(wfs[0], null);
319: wfs[1] = vFilter.getLPSynWaveForm(wfs[1], null);
320: } else if (subb_HL.l2Norm < 0f) {
321: subb_HL.calcBasisWaveForms(wfs);
322: wfs[0] = hFilter.getHPSynWaveForm(wfs[0], null);
323: wfs[1] = vFilter.getLPSynWaveForm(wfs[1], null);
324: } else if (subb_LH.l2Norm < 0f) {
325: subb_LH.calcBasisWaveForms(wfs);
326: wfs[0] = hFilter.getLPSynWaveForm(wfs[0], null);
327: wfs[1] = vFilter.getHPSynWaveForm(wfs[1], null);
328: } else if (subb_HH.l2Norm < 0f) {
329: subb_HH.calcBasisWaveForms(wfs);
330: wfs[0] = hFilter.getHPSynWaveForm(wfs[0], null);
331: wfs[1] = vFilter.getHPSynWaveForm(wfs[1], null);
332: } else {
333: // There is an error! If all childs have
334: // non-negative l2norm, then this node should have
335: // non-negative l2norm
336: throw new Error("You have found a bug in JJ2000!");
337: }
338: } else {
339: // This is a leaf, just use diracs (null is
340: // equivalent to dirac)
341: wfs[0] = new float[1];
342: wfs[0][0] = 1.0f;
343: wfs[1] = new float[1];
344: wfs[1][0] = 1.0f;
345: }
346:
347: } else {
348: // This is an error! The calcBasisWaveForms() method is
349: // never called on an element with non-negative l2norm
350: throw new Error("You have found a bug in JJ2000!");
351: }
352: }
353:
354: /**
355: * Assigns the given L2-norm to the first leaf that does not have
356: * an L2-norm value yet (i.e. l2norm is negative). The search is
357: * done recursively and in the same order as that of the
358: * calcBasisWaveForms() method, so that this method is used to
359: * assigne the l2norm of the previously computed waveforms.
360: *
361: * <P>This method can not be called on an element that ahs a
362: * non-negative value in l2Norm, since that means that we are
363: * done.
364: *
365: * @param l2n The L2-norm to assign.
366: *
367: *
368: * */
369: private void assignL2Norm(float l2n) {
370: if (l2Norm < 0) {
371: // We are not finished with this element yet
372: if (isNode) {
373: // We are on a node => search on childs
374: if (subb_LL.l2Norm < 0f) {
375: subb_LL.assignL2Norm(l2n);
376: } else if (subb_HL.l2Norm < 0f) {
377: subb_HL.assignL2Norm(l2n);
378: } else if (subb_LH.l2Norm < 0f) {
379: subb_LH.assignL2Norm(l2n);
380: } else if (subb_HH.l2Norm < 0f) {
381: subb_HH.assignL2Norm(l2n);
382: // If child now is done, we are done
383: if (subb_HH.l2Norm >= 0f) {
384: l2Norm = 0f; // We are on a node, any non-neg value OK
385: }
386: } else {
387: // There is an error! If all childs have
388: // non-negative l2norm, then this node should have
389: // non-negative l2norm
390: throw new Error("You have found a bug in JJ2000!");
391: }
392: } else {
393: // This is a leaf, assign the L2-norm
394: l2Norm = l2n;
395: }
396:
397: } else {
398: // This is an error! The assignL2Norm() method is
399: // never called on an element with non-negative l2norm
400: throw new Error("You have found a bug in JJ2000!");
401: }
402: }
403:
404: /**
405: * Calculates the L2-norm of the sythesis waveforms of every leaf
406: * in the tree. This method should only be called on the root
407: * element.
408: *
409: *
410: * */
411: private void calcL2Norms() {
412: int i;
413: float wfs[][] = new float[2][];
414: double acc;
415: float l2n;
416:
417: // While we are not done on the root element, compute basis
418: // functions and assign L2-norm
419: while (l2Norm < 0f) {
420: calcBasisWaveForms(wfs);
421: // Compute line L2-norm, which is the product of the line
422: // and column L2-norms
423: acc = 0.0;
424: for (i = wfs[0].length - 1; i >= 0; i--) {
425: acc += wfs[0][i] * wfs[0][i];
426: }
427: l2n = (float) Math.sqrt(acc);
428: // Compute column L2-norm
429: acc = 0.0;
430: for (i = wfs[1].length - 1; i >= 0; i--) {
431: acc += wfs[1][i] * wfs[1][i];
432: }
433: l2n *= (float) Math.sqrt(acc);
434: // Release waveforms
435: wfs[0] = null;
436: wfs[1] = null;
437: // Assign the value
438: assignL2Norm(l2n);
439: }
440: }
441:
442: /**
443: * This function returns the horizontal wavelet filter relevant to this
444: * subband
445: *
446: * @return The horizontal wavelet filter
447: *
448: *
449: */
450: public WaveletFilter getHorWFilter() {
451: return hFilter;
452: }
453:
454: /**
455: * This function returns the vertical wavelet filter relevant to this
456: * subband
457: *
458: * @return The vertical wavelet filter
459: *
460: *
461: */
462: public WaveletFilter getVerWFilter() {
463: return hFilter;
464: }
465: }
|