001: /*
002: * $RCSfile: InvWTFull.java,v $
003: * $Revision: 1.1 $
004: * $Date: 2005/02/11 05:02:32 $
005: * $State: Exp $
006: *
007: * Class: InvWTFull
008: *
009: * Description: This class implements a full page inverse DWT for
010: * int and float data.
011: *
012: * the InvWTFullInt and InvWTFullFloat
013: * classes by Bertrand Berthelot, Apr-19-1999
014: *
015: *
016: * COPYRIGHT:
017: *
018: * This software module was originally developed by Raphaël Grosbois and
019: * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
020: * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
021: * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
022: * Centre France S.A) in the course of development of the JPEG2000
023: * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
024: * software module is an implementation of a part of the JPEG 2000
025: * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
026: * Systems AB and Canon Research Centre France S.A (collectively JJ2000
027: * Partners) agree not to assert against ISO/IEC and users of the JPEG
028: * 2000 Standard (Users) any of their rights under the copyright, not
029: * including other intellectual property rights, for this software module
030: * with respect to the usage by ISO/IEC and Users of this software module
031: * or modifications thereof for use in hardware or software products
032: * claiming conformance to the JPEG 2000 Standard. Those intending to use
033: * this software module in hardware or software products are advised that
034: * their use may infringe existing patents. The original developers of
035: * this software module, JJ2000 Partners and ISO/IEC assume no liability
036: * for use of this software module or modifications thereof. No license
037: * or right to this software module is granted for non JPEG 2000 Standard
038: * conforming products. JJ2000 Partners have full right to use this
039: * software module for his/her own purpose, assign or donate this
040: * software module to any third party and to inhibit third parties from
041: * using this software module for non JPEG 2000 Standard conforming
042: * products. This copyright notice must be included in all copies or
043: * derivative works of this software module.
044: *
045: * Copyright (c) 1999/2000 JJ2000 Partners.
046: * */
047: package jj2000.j2k.wavelet.synthesis;
048:
049: import java.awt.Point;
050:
051: import jj2000.j2k.wavelet.*;
052: import jj2000.j2k.decoder.*;
053: import jj2000.j2k.image.*;
054: import jj2000.j2k.util.*;
055:
056: /**
057: * This class implements the InverseWT with the full-page approach for int and
058: * float data.
059: *
060: * <P>The image can be reconstructed at different (image) resolution levels
061: * indexed from the lowest resolution available for each tile-component. This
062: * is controlled by the setImgResLevel() method.
063: *
064: * <P>Note: Image resolution level indexes may differ from tile-component
065: * resolution index. They are indeed indexed starting from the lowest number
066: * of decomposition levels of each component of each tile.
067: *
068: * <P>Example: For an image (1 tile) with 2 components (component 0 having 2
069: * decomposition levels and component 1 having 3 decomposition levels), the
070: * first (tile-) component has 3 resolution levels and the second one has 4
071: * resolution levels, whereas the image has only 3 resolution levels
072: * available.
073: *
074: * <P>This implementation does not support progressive data, all data is
075: * considered to be non-progressive (i.e. "final" data) and the 'progressive'
076: * attribute of the 'DataBlk' class is always set to false, see the 'DataBlk'
077: * class.
078: *
079: * @see DataBlk
080: * */
081: public class InvWTFull extends InverseWT {
082:
083: /** Reference to the ProgressWatch instance if any */
084: private ProgressWatch pw = null;
085:
086: /** The total number of code-blocks to decode */
087: private int cblkToDecode = 0;
088:
089: /** The number of already decoded code-blocks */
090: private int nDecCblk = 0;
091:
092: /** the code-block buffer's source i.e. the quantizer */
093: private CBlkWTDataSrcDec src;
094:
095: /** Current data type */
096: private int dtype;
097:
098: /**
099: * block storing the reconstructed image for each component
100: */
101: private DataBlk reconstructedComps[];
102:
103: /** Number of decomposition levels in each component */
104: private int[] ndl;
105:
106: /**
107: * The reversible flag for each component in each tile. The first index is
108: * the tile index, the second one is the component index. The
109: * reversibility of the components for each tile are calculated on a as
110: * needed basis.
111: * */
112: private boolean reversible[][];
113:
114: /**
115: * Initializes this object with the given source of wavelet
116: * coefficients. It initializes the resolution level for full resolutioin
117: * reconstruction.
118: *
119: * @param src from where the wavelet coefficinets should be
120: * obtained.
121: *
122: * @param decSpec The decoder specifications
123: * */
124: public InvWTFull(CBlkWTDataSrcDec src, DecoderSpecs decSpec) {
125: super (src, decSpec);
126: this .src = src;
127:
128: int nc = src.getNumComps();
129: reconstructedComps = new DataBlk[nc];
130: ndl = new int[nc];
131: pw = FacilityManager.getProgressWatch();
132: }
133:
134: /**
135: * Returns the reversibility of the current subband. It computes
136: * iteratively the reversibility of the child subbands. For each subband
137: * it tests the reversibility of the horizontal and vertical synthesis
138: * filters used to reconstruct this subband.
139: *
140: * @param subband The current subband.
141: *
142: * @return true if all the filters used to reconstruct the current
143: * subband are reversible
144: * */
145: private boolean isSubbandReversible(Subband subband) {
146: if (subband.isNode) {
147: // It's reversible if the filters to obtain the 4 subbands are
148: // reversible and the ones for this one are reversible too.
149: return isSubbandReversible(subband.getLL())
150: && isSubbandReversible(subband.getHL())
151: && isSubbandReversible(subband.getLH())
152: && isSubbandReversible(subband.getHH())
153: && ((SubbandSyn) subband).hFilter.isReversible()
154: && ((SubbandSyn) subband).vFilter.isReversible();
155: } else {
156: // Leaf subband. Reversibility of data depends on source, so say
157: // it's true
158: return true;
159: }
160: }
161:
162: /**
163: * Returns the reversibility of the wavelet transform for the specified
164: * component, in the current tile. A wavelet transform is reversible when
165: * it is suitable for lossless and lossy-to-lossless compression.
166: *
167: * @param t The index of the tile.
168: *
169: * @param c The index of the component.
170: *
171: * @return true is the wavelet transform is reversible, false if not.
172: * */
173: public boolean isReversible(int t, int c) {
174: if (reversible[t] == null) {
175: // Reversibility not yet calculated for this tile
176: reversible[t] = new boolean[getNumComps()];
177: for (int i = reversible.length - 1; i >= 0; i--) {
178: reversible[t][i] = isSubbandReversible(src
179: .getSynSubbandTree(t, i));
180: }
181: }
182: return reversible[t][c];
183: }
184:
185: /**
186: * Returns the number of bits, referred to as the "range bits",
187: * corresponding to the nominal range of the data in the specified
188: * component.
189: *
190: * <P>The returned value corresponds to the nominal dynamic range of the
191: * reconstructed image data, as long as the getNomRangeBits() method of
192: * the source returns a value corresponding to the nominal dynamic range
193: * of the image data and not not of the wavelet coefficients.
194: *
195: * <P>If this number is <i>b</b> then for unsigned data the nominal range
196: * is between 0 and 2^b-1, and for signed data it is between -2^(b-1) and
197: * 2^(b-1)-1.
198: *
199: * @param c The index of the component.
200: *
201: * @return The number of bits corresponding to the nominal range of the
202: * data.
203: * */
204: public int getNomRangeBits(int c) {
205: return src.getNomRangeBits(c);
206: }
207:
208: /**
209: * Returns the position of the fixed point in the specified
210: * component. This is the position of the least significant integral
211: * (i.e. non-fractional) bit, which is equivalent to the number of
212: * fractional bits. For instance, for fixed-point values with 2 fractional
213: * bits, 2 is returned. For floating-point data this value does not apply
214: * and 0 should be returned. Position 0 is the position of the least
215: * significant bit in the data.
216: *
217: * <P>This default implementation assumes that the wavelet transform does
218: * not modify the fixed point. If that were the case this method should be
219: * overriden.
220: *
221: * @param c The index of the component.
222: *
223: * @return The position of the fixed-point, which is the same as the
224: * number of fractional bits. For floating-point data 0 is returned.
225: * */
226: public int getFixedPoint(int c) {
227: return src.getFixedPoint(c);
228: }
229:
230: /**
231: * Returns a block of image data containing the specifed rectangular area,
232: * in the specified component, as a reference to the internal buffer (see
233: * below). The rectangular area is specified by the coordinates and
234: * dimensions of the 'blk' object.
235: *
236: * <p>The area to return is specified by the 'ulx', 'uly', 'w' and 'h'
237: * members of the 'blk' argument. These members are not modified by this
238: * method.</p>
239: *
240: * <p>The data returned by this method can be the data in the internal
241: * buffer of this object, if any, and thus can not be modified by the
242: * caller. The 'offset' and 'scanw' of the returned data can be
243: * arbitrary. See the 'DataBlk' class.</p>
244: *
245: * <p>The returned data has its 'progressive' attribute unset
246: * (i.e. false).</p>
247: *
248: * @param blk Its coordinates and dimensions specify the area to return.
249: *
250: * @param c The index of the component from which to get the data.
251: *
252: * @return The requested DataBlk
253: *
254: * @see #getInternCompData
255: * */
256: public final DataBlk getInternCompData(DataBlk blk, int c) {
257: int tIdx = getTileIdx();
258: if (src.getSynSubbandTree(tIdx, c).getHorWFilter() == null) {
259: dtype = DataBlk.TYPE_INT;
260: } else {
261: dtype = src.getSynSubbandTree(tIdx, c).getHorWFilter()
262: .getDataType();
263: }
264:
265: //If the source image has not been decomposed
266: if (reconstructedComps[c] == null) {
267: //Allocate component data buffer
268: switch (dtype) {
269: case DataBlk.TYPE_FLOAT:
270: reconstructedComps[c] = new DataBlkFloat(0, 0,
271: getTileCompWidth(tIdx, c), getTileCompHeight(
272: tIdx, c));
273: break;
274: case DataBlk.TYPE_INT:
275: reconstructedComps[c] = new DataBlkInt(0, 0,
276: getTileCompWidth(tIdx, c), getTileCompHeight(
277: tIdx, c));
278: break;
279: }
280: //Reconstruct source image
281: waveletTreeReconstruction(reconstructedComps[c], src
282: .getSynSubbandTree(tIdx, c), c);
283: if (pw != null && c == src.getNumComps() - 1) {
284: pw.terminateProgressWatch();
285: }
286: }
287:
288: if (blk.getDataType() != dtype) {
289: if (dtype == DataBlk.TYPE_INT) {
290: blk = new DataBlkInt(blk.ulx, blk.uly, blk.w, blk.h);
291: } else {
292: blk = new DataBlkFloat(blk.ulx, blk.uly, blk.w, blk.h);
293: }
294: }
295: // Set the reference to the internal buffer
296: blk.setData(reconstructedComps[c].getData());
297: blk.offset = reconstructedComps[c].w * blk.uly + blk.ulx;
298: blk.scanw = reconstructedComps[c].w;
299: blk.progressive = false;
300: return blk;
301: }
302:
303: /**
304: * Returns a block of image data containing the specifed rectangular area,
305: * in the specified component, as a copy (see below). The rectangular area
306: * is specified by the coordinates and dimensions of the 'blk' object.
307: *
308: * <P>The area to return is specified by the 'ulx', 'uly', 'w' and 'h'
309: * members of the 'blk' argument. These members are not modified by this
310: * method.
311: *
312: * <P>The data returned by this method is always a copy of the internal
313: * data of this object, if any, and it can be modified "in place" without
314: * any problems after being returned. The 'offset' of the returned data is
315: * 0, and the 'scanw' is the same as the block's width. See the 'DataBlk'
316: * class.
317: *
318: * <P>If the data array in 'blk' is <tt>null</tt>, then a new one is
319: * created. If the data array is not <tt>null</tt> then it must be big
320: * enough to contain the requested area.
321: *
322: * <P>The returned data always has its 'progressive' attribute unset (i.e
323: * false)
324: *
325: * @param blk Its coordinates and dimensions specify the area to
326: * return. If it contains a non-null data array, then it must be large
327: * enough. If it contains a null data array a new one is created. The
328: * fields in this object are modified to return the data.
329: *
330: * @param c The index of the component from which to get the data.
331: *
332: * @return The requested DataBlk
333: *
334: * @see #getCompData
335: * */
336: public DataBlk getCompData(DataBlk blk, int c) {
337: int j;
338: Object src_data, dst_data;
339: int src_data_int[], dst_data_int[];
340: float src_data_float[], dst_data_float[];
341:
342: // To keep compiler happy
343: dst_data = null;
344:
345: // Ensure output buffer
346: switch (blk.getDataType()) {
347: case DataBlk.TYPE_INT:
348: dst_data_int = (int[]) blk.getData();
349: if (dst_data_int == null
350: || dst_data_int.length < blk.w * blk.h) {
351: dst_data_int = new int[blk.w * blk.h];
352: }
353: dst_data = dst_data_int;
354: break;
355: case DataBlk.TYPE_FLOAT:
356: dst_data_float = (float[]) blk.getData();
357: if (dst_data_float == null
358: || dst_data_float.length < blk.w * blk.h) {
359: dst_data_float = new float[blk.w * blk.h];
360: }
361: dst_data = dst_data_float;
362: break;
363: }
364:
365: // Use getInternCompData() to get the data, since getInternCompData()
366: // returns reference to internal buffer, we must copy it.
367: blk = getInternCompData(blk, c);
368:
369: // Copy the data
370: blk.setData(dst_data);
371: blk.offset = 0;
372: blk.scanw = blk.w;
373: return blk;
374: }
375:
376: /**
377: * Performs the 2D inverse wavelet transform on a subband of the image, on
378: * the specified component. This method will successively perform 1D
379: * filtering steps on all columns and then all lines of the subband.
380: *
381: * @param db the buffer for the image/wavelet data.
382: *
383: * @param sb The subband to reconstruct.
384: *
385: * @param c The index of the component to reconstruct
386: * */
387: private void wavelet2DReconstruction(DataBlk db, SubbandSyn sb,
388: int c) {
389: Object data;
390: Object buf;
391: int ulx, uly, w, h;
392: int i, j, k;
393: int offset;
394:
395: // If subband is empty (i.e. zero size) nothing to do
396: if (sb.w == 0 || sb.h == 0) {
397: return;
398: }
399:
400: data = db.getData();
401:
402: ulx = sb.ulx;
403: uly = sb.uly;
404: w = sb.w;
405: h = sb.h;
406:
407: buf = null; // To keep compiler happy
408:
409: switch (sb.getHorWFilter().getDataType()) {
410: case DataBlk.TYPE_INT:
411: buf = new int[(w >= h) ? w : h];
412: break;
413: case DataBlk.TYPE_FLOAT:
414: buf = new float[(w >= h) ? w : h];
415: break;
416: }
417:
418: //Perform the horizontal reconstruction
419: offset = (uly - db.uly) * db.w + ulx - db.ulx;
420: if (sb.ulcx % 2 == 0) { // start index is even => use LPF
421: for (i = 0; i < h; i++, offset += db.w) {
422: System.arraycopy(data, offset, buf, 0, w);
423: sb.hFilter.synthetize_lpf(buf, 0, (w + 1) / 2, 1, buf,
424: (w + 1) / 2, w / 2, 1, data, offset, 1);
425: }
426: } else { // start index is odd => use HPF
427: for (i = 0; i < h; i++, offset += db.w) {
428: System.arraycopy(data, offset, buf, 0, w);
429: sb.hFilter.synthetize_hpf(buf, 0, w / 2, 1, buf, w / 2,
430: (w + 1) / 2, 1, data, offset, 1);
431: }
432: }
433:
434: //Perform the vertical reconstruction
435: offset = (uly - db.uly) * db.w + ulx - db.ulx;
436: switch (sb.getVerWFilter().getDataType()) {
437: case DataBlk.TYPE_INT:
438: int data_int[],
439: buf_int[];
440: data_int = (int[]) data;
441: buf_int = (int[]) buf;
442: if (sb.ulcy % 2 == 0) { // start index is even => use LPF
443: for (j = 0; j < w; j++, offset++) {
444: for (i = h - 1, k = offset + i * db.w; i >= 0; i--, k -= db.w)
445: buf_int[i] = data_int[k];
446: sb.vFilter.synthetize_lpf(buf, 0, (h + 1) / 2, 1,
447: buf, (h + 1) / 2, h / 2, 1, data, offset,
448: db.w);
449: }
450: } else { // start index is odd => use HPF
451: for (j = 0; j < w; j++, offset++) {
452: for (i = h - 1, k = offset + i * db.w; i >= 0; i--, k -= db.w)
453: buf_int[i] = data_int[k];
454: sb.vFilter.synthetize_hpf(buf, 0, h / 2, 1, buf,
455: h / 2, (h + 1) / 2, 1, data, offset, db.w);
456: }
457: }
458: break;
459: case DataBlk.TYPE_FLOAT:
460: float data_float[],
461: buf_float[];
462: data_float = (float[]) data;
463: buf_float = (float[]) buf;
464: if (sb.ulcy % 2 == 0) { // start index is even => use LPF
465: for (j = 0; j < w; j++, offset++) {
466: for (i = h - 1, k = offset + i * db.w; i >= 0; i--, k -= db.w)
467: buf_float[i] = data_float[k];
468: sb.vFilter.synthetize_lpf(buf, 0, (h + 1) / 2, 1,
469: buf, (h + 1) / 2, h / 2, 1, data, offset,
470: db.w);
471: }
472: } else { // start index is odd => use HPF
473: for (j = 0; j < w; j++, offset++) {
474: for (i = h - 1, k = offset + i * db.w; i >= 0; i--, k -= db.w)
475: buf_float[i] = data_float[k];
476: sb.vFilter.synthetize_hpf(buf, 0, h / 2, 1, buf,
477: h / 2, (h + 1) / 2, 1, data, offset, db.w);
478: }
479: }
480: break;
481: }
482: }
483:
484: /**
485: * Performs the inverse wavelet transform on the whole component. It
486: * iteratively reconstructs the subbands from leaves up to the root
487: * node. This method is recursive, the first call to it the 'sb' must be
488: * the root of the subband tree. The method will then process the entire
489: * subband tree by calling itslef recursively.
490: *
491: * @param img The buffer for the image/wavelet data.
492: *
493: * @param sb The subband to reconstruct.
494: *
495: * @param c The index of the component to reconstruct
496: * */
497: private void waveletTreeReconstruction(DataBlk img, SubbandSyn sb,
498: int c) {
499:
500: DataBlk subbData;
501:
502: // If the current subband is a leaf then get the data from the source
503: if (!sb.isNode) {
504: int i, m, n;
505: Object src_data, dst_data;
506: Point ncblks;
507:
508: if (sb.w == 0 || sb.h == 0) {
509: return; // If empty subband do nothing
510: }
511:
512: // Get all code-blocks in subband
513: if (dtype == DataBlk.TYPE_INT) {
514: subbData = new DataBlkInt();
515: } else {
516: subbData = new DataBlkFloat();
517: }
518: ncblks = sb.numCb;
519: dst_data = img.getData();
520: for (m = 0; m < ncblks.y; m++) {
521: for (n = 0; n < ncblks.x; n++) {
522: subbData = src.getInternCodeBlock(c, m, n, sb,
523: subbData);
524: src_data = subbData.getData();
525: if (pw != null) {
526: nDecCblk++;
527: pw.updateProgressWatch(nDecCblk, null);
528: }
529: // Copy the data line by line
530: for (i = subbData.h - 1; i >= 0; i--) {
531: System.arraycopy(src_data, subbData.offset + i
532: * subbData.scanw, dst_data,
533: (subbData.uly + i) * img.w
534: + subbData.ulx, subbData.w);
535: }
536: }
537: }
538: } else if (sb.isNode) {
539: // Reconstruct the lower resolution levels if the current subbands
540: // is a node
541:
542: //Perform the reconstruction of the LL subband
543: waveletTreeReconstruction(img, (SubbandSyn) sb.getLL(), c);
544:
545: if (sb.resLvl <= reslvl - maxImgRes + ndl[c]) {
546: //Reconstruct the other subbands
547: waveletTreeReconstruction(img, (SubbandSyn) sb.getHL(),
548: c);
549: waveletTreeReconstruction(img, (SubbandSyn) sb.getLH(),
550: c);
551: waveletTreeReconstruction(img, (SubbandSyn) sb.getHH(),
552: c);
553:
554: //Perform the 2D wavelet decomposition of the current subband
555: wavelet2DReconstruction(img, (SubbandSyn) sb, c);
556: }
557: }
558: }
559:
560: /**
561: * Returns the implementation type of this wavelet transform, WT_IMPL_FULL
562: * (full-page based transform). All components return the same.
563: *
564: * @param c The index of the component.
565: *
566: * @return WT_IMPL_FULL
567: *
568: * @see WaveletTransform#WT_IMPL_FULL
569: * */
570: public int getImplementationType(int c) {
571: return WaveletTransform.WT_IMPL_FULL;
572: }
573:
574: /**
575: * Changes the current tile, given the new indexes. An
576: * IllegalArgumentException is thrown if the indexes do not correspond to
577: * a valid tile.
578: *
579: * @param x The horizontal index of the tile.
580: *
581: * @param y The vertical index of the new tile.
582: * */
583: public void setTile(int x, int y) {
584: int i;
585:
586: // Change tile
587: super .setTile(x, y);
588:
589: int nc = src.getNumComps();
590: int tIdx = src.getTileIdx();
591: for (int c = 0; c < nc; c++) {
592: ndl[c] = src.getSynSubbandTree(tIdx, c).resLvl;
593: }
594:
595: // Reset the decomposed component buffers.
596: if (reconstructedComps != null) {
597: for (i = reconstructedComps.length - 1; i >= 0; i--) {
598: reconstructedComps[i] = null;
599: }
600: }
601:
602: cblkToDecode = 0;
603: SubbandSyn root, sb;
604: for (int c = 0; c < nc; c++) {
605: root = src.getSynSubbandTree(tIdx, c);
606: for (int r = 0; r <= reslvl - maxImgRes + root.resLvl; r++) {
607: if (r == 0) {
608: sb = (SubbandSyn) root.getSubbandByIdx(0, 0);
609: if (sb != null)
610: cblkToDecode += sb.numCb.x * sb.numCb.y;
611: } else {
612: sb = (SubbandSyn) root.getSubbandByIdx(r, 1);
613: if (sb != null)
614: cblkToDecode += sb.numCb.x * sb.numCb.y;
615: sb = (SubbandSyn) root.getSubbandByIdx(r, 2);
616: if (sb != null)
617: cblkToDecode += sb.numCb.x * sb.numCb.y;
618: sb = (SubbandSyn) root.getSubbandByIdx(r, 3);
619: if (sb != null)
620: cblkToDecode += sb.numCb.x * sb.numCb.y;
621: }
622: } // Loop on resolution levels
623: } // Loop on components
624: nDecCblk = 0;
625:
626: if (pw != null) {
627: pw.initProgressWatch(0, cblkToDecode, "Decoding tile "
628: + tIdx + "...");
629: }
630: }
631:
632: /**
633: * Advances to the next tile, in standard scan-line order (by rows then
634: * columns). An 'NoNextElementException' is thrown if the current tile is
635: * the last one (i.e. there is no next tile).
636: * */
637: public void nextTile() {
638: int i;
639:
640: // Change tile
641: super .nextTile();
642:
643: int nc = src.getNumComps();
644: int tIdx = src.getTileIdx();
645: for (int c = 0; c < nc; c++) {
646: ndl[c] = src.getSynSubbandTree(tIdx, c).resLvl;
647: }
648:
649: // Reset the decomposed component buffers.
650: if (reconstructedComps != null) {
651: for (i = reconstructedComps.length - 1; i >= 0; i--) {
652: reconstructedComps[i] = null;
653: }
654: }
655: }
656:
657: }
|