0001: /*
0002: * $RCSfile: ForwWTFull.java,v $
0003: * $Revision: 1.1 $
0004: * $Date: 2005/02/11 05:02:31 $
0005: * $State: Exp $
0006: *
0007: * Class: ForwWTFull
0008: *
0009: * Description: This class implements the full page
0010: * forward wavelet transform for both integer
0011: * and floating point implementations.
0012: *
0013: *
0014: *
0015: * COPYRIGHT:
0016: *
0017: * This software module was originally developed by Raphaël Grosbois and
0018: * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
0019: * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
0020: * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
0021: * Centre France S.A) in the course of development of the JPEG2000
0022: * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
0023: * software module is an implementation of a part of the JPEG 2000
0024: * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
0025: * Systems AB and Canon Research Centre France S.A (collectively JJ2000
0026: * Partners) agree not to assert against ISO/IEC and users of the JPEG
0027: * 2000 Standard (Users) any of their rights under the copyright, not
0028: * including other intellectual property rights, for this software module
0029: * with respect to the usage by ISO/IEC and Users of this software module
0030: * or modifications thereof for use in hardware or software products
0031: * claiming conformance to the JPEG 2000 Standard. Those intending to use
0032: * this software module in hardware or software products are advised that
0033: * their use may infringe existing patents. The original developers of
0034: * this software module, JJ2000 Partners and ISO/IEC assume no liability
0035: * for use of this software module or modifications thereof. No license
0036: * or right to this software module is granted for non JPEG 2000 Standard
0037: * conforming products. JJ2000 Partners have full right to use this
0038: * software module for his/her own purpose, assign or donate this
0039: * software module to any third party and to inhibit third parties from
0040: * using this software module for non JPEG 2000 Standard conforming
0041: * products. This copyright notice must be included in all copies or
0042: * derivative works of this software module.
0043: *
0044: * Copyright (c) 1999/2000 JJ2000 Partners.
0045: * */
0046: package jj2000.j2k.wavelet.analysis;
0047:
0048: import java.awt.Point;
0049:
0050: import jj2000.j2k.codestream.*;
0051: import jj2000.j2k.entropy.*;
0052: import jj2000.j2k.wavelet.*; //import jj2000.j2k.encoder.*;
0053: import jj2000.j2k.image.*;
0054: import jj2000.j2k.util.*;
0055: import jj2000.j2k.*;
0056:
0057: import com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageWriteParamJava;
0058:
0059: /**
0060: * This class implements the ForwardWT with the full-page approach to be used
0061: * either with integer or floating-point filters
0062: * */
0063: public class ForwWTFull extends ForwardWT {
0064:
0065: /** Boolean to know if one are currently dealing with int or float
0066: data. */
0067: private boolean intData;
0068:
0069: /**
0070: * The subband trees for each component, in each tile. The array is
0071: * allocated by the constructor of this class. This array is updated by
0072: * the getSubbandTree() method, an a as needed basis. The first index is
0073: * the tile index (in lexicographical order) and the second index is the
0074: * component index.
0075: *
0076: * <P>The subband tree for a component in the current tile is created on
0077: * the first call to getSubbandTree() for that component, in the current
0078: * tile. Before that the element in 'subbTrees' is null.
0079: * */
0080: private SubbandAn subbTrees[][];
0081:
0082: /** The source of image data */
0083: private BlkImgDataSrc src;
0084:
0085: /** The horizontal coordinate of the code-block partition origin on the
0086: reference grid */
0087: private int cb0x;
0088:
0089: /** The vertical coordinate of the code-block partition on the reference
0090: grid */
0091: private int cb0y;
0092:
0093: /** The number of decomposition levels specification */
0094: private IntegerSpec dls;
0095:
0096: /** Wavelet filters for all components and tiles */
0097: private AnWTFilterSpec filters;
0098:
0099: /** The code-block size specifications */
0100: private CBlkSizeSpec cblks;
0101:
0102: /** The precinct partition specifications */
0103: private PrecinctSizeSpec pss;
0104:
0105: /** Block storing the full band decomposition for each component. */
0106: private DataBlk decomposedComps[];
0107:
0108: /**
0109: * The horizontal index of the last code-block "sent" in the current
0110: * subband in each component. It should be -1 if none have been sent yet.
0111: * */
0112: private int lastn[];
0113:
0114: /**
0115: * The vertical index of the last code-block "sent" in the current subband
0116: * in each component. It should be 0 if none have been sent yet.
0117: * */
0118: private int lastm[];
0119:
0120: /** The subband being dealt with in each component */
0121: SubbandAn currentSubband[];
0122:
0123: /** Cache object to avoid excessive allocation/deallocation. This variable
0124: * makes the class inheritently thread unsafe. */
0125: Point ncblks;
0126:
0127: /**
0128: * Initializes this object with the given source of image data and with
0129: * all the decompositon parameters
0130: *
0131: * @param src From where the image data should be obtained.
0132: *
0133: * @param encSpec The encoder specifications
0134: *
0135: * @param pox The horizontal coordinate of the cell and code-block
0136: * partition origin with respect to the canvas origin, on the reference
0137: * grid.
0138: *
0139: * @param poy The vertical coordinate of the cell and code-block partition
0140: * origin with respect to the canvas origin, on the reference grid.
0141: *
0142: * @see ForwardWT
0143: * */
0144: public ForwWTFull(BlkImgDataSrc src, J2KImageWriteParamJava wp,
0145: int pox, int poy) {
0146: super (src);
0147: this .src = src;
0148:
0149: this .cb0x = cb0x;
0150: this .cb0y = cb0y;
0151: this .dls = wp.getDecompositionLevel();
0152: this .filters = wp.getFilters();
0153: this .cblks = wp.getCodeBlockSize();
0154: this .pss = wp.getPrecinctPartition();
0155:
0156: int ncomp = src.getNumComps();
0157: int ntiles = src.getNumTiles();
0158:
0159: currentSubband = new SubbandAn[ncomp];
0160: decomposedComps = new DataBlk[ncomp];
0161: subbTrees = new SubbandAn[ntiles][ncomp];
0162: lastn = new int[ncomp];
0163: lastm = new int[ncomp];
0164: }
0165:
0166: /**
0167: * Returns the implementation type of this wavelet transform, WT_IMPL_FULL
0168: * (full-page based transform). All components return the same.
0169: *
0170: * @param c The index of the component.
0171: *
0172: * @return WT_IMPL_FULL
0173: * */
0174: public int getImplementationType(int c) {
0175: return WaveletTransform.WT_IMPL_FULL;
0176: }
0177:
0178: /**
0179: * Returns the number of decomposition levels that are applied to the LL
0180: * band, in the specified tile-component. A value of 0 means that no
0181: * wavelet transform is applied.
0182: *
0183: * @param t The tile index
0184: *
0185: * @param c The index of the component.
0186: *
0187: * @return The number of decompositions applied to the LL band (0 for no
0188: * wavelet transform).
0189: * */
0190: public int getDecompLevels(int t, int c) {
0191: return ((Integer) dls.getTileCompVal(t, c)).intValue();
0192: }
0193:
0194: /**
0195: * Returns the wavelet tree decomposition. Actually JPEG 2000 part 1 only
0196: * supports WT_DECOMP_DYADIC decomposition.
0197: *
0198: * @param t The tile-index
0199: *
0200: * @param c The index of the component.
0201: *
0202: * @return The wavelet decomposition.
0203: * */
0204: public int getDecomp(int t, int c) {
0205: return WT_DECOMP_DYADIC;
0206: }
0207:
0208: /**
0209: * Returns the horizontal analysis wavelet filters used in each level, for
0210: * the specified component and tile. The first element in the array is the
0211: * filter used to obtain the lowest resolution (resolution level 0)
0212: * subbands (i.e. lowest frequency LL subband), the second element is the
0213: * one used to generate the resolution level 1 subbands, and so on. If
0214: * there are less elements in the array than the number of resolution
0215: * levels, then the last one is assumed to repeat itself.
0216: *
0217: * <P>The returned filters are applicable only to the specified component
0218: * and in the current tile.
0219: *
0220: * <P>The resolution level of a subband is the resolution level to which a
0221: * subband contributes, which is different from its decomposition level.
0222: *
0223: * @param t The index of the tile for which to return the filters.
0224: *
0225: * @param c The index of the component for which to return the filters.
0226: *
0227: * @return The horizontal analysis wavelet filters used in each level.
0228: * */
0229: public AnWTFilter[] getHorAnWaveletFilters(int t, int c) {
0230: return filters.getHFilters(t, c);
0231: }
0232:
0233: /**
0234: * Returns the vertical analysis wavelet filters used in each level, for
0235: * the specified component and tile. The first element in the array is the
0236: * filter used to obtain the lowest resolution (resolution level 0)
0237: * subbands (i.e. lowest frequency LL subband), the second element is the
0238: * one used to generate the resolution level 1 subbands, and so on. If
0239: * there are less elements in the array than the number of resolution
0240: * levels, then the last one is assumed to repeat itself.
0241: *
0242: * <P>The returned filters are applicable only to the specified component
0243: * and in the current tile.
0244: *
0245: * <P>The resolution level of a subband is the resolution level to which a
0246: * subband contributes, which is different from its decomposition level.
0247: *
0248: * @param t The index of the tile for which to return the filters.
0249: *
0250: * @param c The index of the component for which to return the filters.
0251: *
0252: * @return The vertical analysis wavelet filters used in each level.
0253: * */
0254: public AnWTFilter[] getVertAnWaveletFilters(int t, int c) {
0255: return filters.getVFilters(t, c);
0256: }
0257:
0258: /**
0259: * Returns the reversibility of the wavelet transform for the specified
0260: * component and tile. A wavelet transform is reversible when it is
0261: * suitable for lossless and lossy-to-lossless compression.
0262: *
0263: * @param t The index of the tile.
0264: *
0265: * @param c The index of the component.
0266: *
0267: * @return true is the wavelet transform is reversible, false if not.
0268: * */
0269: public boolean isReversible(int t, int c) {
0270: return filters.isReversible(t, c);
0271: }
0272:
0273: /**
0274: * Returns the horizontal offset of the code-block partition. Allowable
0275: * values are 0 and 1, nothing else.
0276: * */
0277: public int getCbULX() {
0278: return cb0x;
0279: }
0280:
0281: /**
0282: * Returns the vertical offset of the code-block partition. Allowable
0283: * values are 0 and 1, nothing else.
0284: * */
0285: public int getCbULY() {
0286: return cb0y;
0287: }
0288:
0289: /**
0290: * Returns the position of the fixed point in the specified
0291: * component. This is the position of the least significant integral
0292: * (i.e. non-fractional) bit, which is equivalent to the number of
0293: * fractional bits. For instance, for fixed-point values with 2 fractional
0294: * bits, 2 is returned. For floating-point data this value does not apply
0295: * and 0 should be returned. Position 0 is the position of the least
0296: * significant bit in the data.
0297: *
0298: * @param c The index of the component.
0299: *
0300: * @return The position of the fixed-point, which is the same as the
0301: * number of fractional bits. For floating-point data 0 is returned.
0302: * */
0303: public int getFixedPoint(int c) {
0304: return src.getFixedPoint(c);
0305: }
0306:
0307: /**
0308: * Returns the next code-block in the current tile for the specified
0309: * component. The order in which code-blocks are returned is not
0310: * specified. However each code-block is returned only once and all
0311: * code-blocks will be returned if the method is called 'N' times, where
0312: * 'N' is the number of code-blocks in the tile. After all the code-blocks
0313: * have been returned for the current tile calls to this method will
0314: * return 'null'.
0315: *
0316: * <p>When changing the current tile (through 'setTile()' or 'nextTile()')
0317: * this method will always return the first code-block, as if this method
0318: * was never called before for the new current tile.</p>
0319: *
0320: * <p>The data returned by this method is the data in the internal buffer
0321: * of this object, and thus can not be modified by the caller. The
0322: * 'offset' and 'scanw' of the returned data have, in general, some
0323: * non-zero value. The 'magbits' of the returned data is not set by this
0324: * method and should be ignored. See the 'CBlkWTData' class.</p>
0325: *
0326: * <p>The 'ulx' and 'uly' members of the returned 'CBlkWTData' object
0327: * contain the coordinates of the top-left corner of the block, with
0328: * respect to the tile, not the subband.</p>
0329: *
0330: * @param c The component for which to return the next code-block.
0331: *
0332: * @param cblk If non-null this object will be used to return the new
0333: * code-block. If null a new one will be allocated and returned.
0334: *
0335: * @return The next code-block in the current tile for component 'n', or
0336: * null if all code-blocks for the current tile have been returned.
0337: *
0338: * @see CBlkWTData
0339: * */
0340: public CBlkWTData getNextInternCodeBlock(int c, CBlkWTData cblk) {
0341: int cbm, cbn, cn, cm;
0342: int acb0x, acb0y;
0343: SubbandAn sb;
0344: intData = (filters.getWTDataType(tIdx, c) == DataBlk.TYPE_INT);
0345:
0346: //If the source image has not been decomposed
0347: if (decomposedComps[c] == null) {
0348: int k, w, h;
0349: DataBlk bufblk;
0350: Object dst_data;
0351:
0352: w = getTileCompWidth(tIdx, c);
0353: h = getTileCompHeight(tIdx, c);
0354:
0355: //Get the source image data
0356: if (intData) {
0357: decomposedComps[c] = new DataBlkInt(0, 0, w, h);
0358: bufblk = new DataBlkInt();
0359: } else {
0360: decomposedComps[c] = new DataBlkFloat(0, 0, w, h);
0361: bufblk = new DataBlkFloat();
0362: }
0363:
0364: // Get data from source line by line (this diminishes the memory
0365: // requirements on the data source)
0366: dst_data = decomposedComps[c].getData();
0367: int lstart = getCompULX(c);
0368: bufblk.ulx = lstart;
0369: bufblk.w = w;
0370: bufblk.h = 1;
0371: int kk = getCompULY(c);
0372: for (k = 0; k < h; k++, kk++) {
0373: bufblk.uly = kk;
0374: bufblk.ulx = lstart;
0375: bufblk = src.getInternCompData(bufblk, c);
0376: System.arraycopy(bufblk.getData(), bufblk.offset,
0377: dst_data, k * w, w);
0378: }
0379:
0380: //Decompose source image
0381: waveletTreeDecomposition(decomposedComps[c],
0382: getAnSubbandTree(tIdx, c), c);
0383:
0384: // Make the first subband the current one
0385: currentSubband[c] = getNextSubband(c);
0386:
0387: lastn[c] = -1;
0388: lastm[c] = 0;
0389: }
0390:
0391: // Get the next code-block to "send"
0392: do {
0393: // Calculate number of code-blocks in current subband
0394: ncblks = currentSubband[c].numCb;
0395: // Goto next code-block
0396: lastn[c]++;
0397: if (lastn[c] == ncblks.x) { // Got to end of this row of
0398: // code-blocks
0399: lastn[c] = 0;
0400: lastm[c]++;
0401: }
0402: if (lastm[c] < ncblks.y) {
0403: // Not past the last code-block in the subband, we can return
0404: // this code-block
0405: break;
0406: }
0407: // If we get here we already sent all code-blocks in this subband,
0408: // goto next subband
0409: currentSubband[c] = getNextSubband(c);
0410: lastn[c] = -1;
0411: lastm[c] = 0;
0412: if (currentSubband[c] == null) {
0413: // We don't need the transformed data any more (a priori)
0414: decomposedComps[c] = null;
0415: // All code-blocks from all subbands in the current
0416: // tile have been returned so we return a null
0417: // reference
0418: return null;
0419: }
0420: // Loop to find the next code-block
0421: } while (true);
0422:
0423: // Project code-block partition origin to subband. Since the origin is
0424: // always 0 or 1, it projects to the low-pass side (throught the ceil
0425: // operator) as itself (i.e. no change) and to the high-pass side
0426: // (through the floor operator) as 0, always.
0427: acb0x = cb0x;
0428: acb0y = cb0y;
0429: switch (currentSubband[c].sbandIdx) {
0430: case Subband.WT_ORIENT_LL:
0431: // No need to project since all low-pass => nothing to do
0432: break;
0433: case Subband.WT_ORIENT_HL:
0434: acb0x = 0;
0435: break;
0436: case Subband.WT_ORIENT_LH:
0437: acb0y = 0;
0438: break;
0439: case Subband.WT_ORIENT_HH:
0440: acb0x = 0;
0441: acb0y = 0;
0442: break;
0443: default:
0444: throw new Error("Internal JJ2000 error");
0445: }
0446: // Initialize output code-block
0447: if (cblk == null) {
0448: if (intData) {
0449: cblk = new CBlkWTDataInt();
0450: } else {
0451: cblk = new CBlkWTDataFloat();
0452: }
0453: }
0454: cbn = lastn[c];
0455: cbm = lastm[c];
0456: sb = currentSubband[c];
0457: cblk.n = cbn;
0458: cblk.m = cbm;
0459: cblk.sb = sb;
0460: // Calculate the indexes of first code-block in subband with respect
0461: // to the partitioning origin, to then calculate the position and size
0462: // NOTE: when calculating "floor()" by integer division the dividend
0463: // and divisor must be positive, we ensure that by adding the divisor
0464: // to the dividend and then substracting 1 to the result of the
0465: // division
0466: cn = (sb.ulcx - acb0x + sb.nomCBlkW) / sb.nomCBlkW - 1;
0467: cm = (sb.ulcy - acb0y + sb.nomCBlkH) / sb.nomCBlkH - 1;
0468: if (cbn == 0) { // Left-most code-block, starts where subband starts
0469: cblk.ulx = sb.ulx;
0470: } else {
0471: // Calculate starting canvas coordinate and convert to subb. coords
0472: cblk.ulx = (cn + cbn) * sb.nomCBlkW - (sb.ulcx - acb0x)
0473: + sb.ulx;
0474: }
0475: if (cbm == 0) { // Bottom-most code-block, starts where subband starts
0476: cblk.uly = sb.uly;
0477: } else {
0478: cblk.uly = (cm + cbm) * sb.nomCBlkH - (sb.ulcy - acb0y)
0479: + sb.uly;
0480: }
0481: if (cbn < ncblks.x - 1) {
0482: // Calculate where next code-block starts => width
0483: cblk.w = (cn + cbn + 1) * sb.nomCBlkW - (sb.ulcx - acb0x)
0484: + sb.ulx - cblk.ulx;
0485: } else { // Right-most code-block, ends where subband ends
0486: cblk.w = sb.ulx + sb.w - cblk.ulx;
0487: }
0488: if (cbm < ncblks.y - 1) {
0489: // Calculate where next code-block starts => height
0490: cblk.h = (cm + cbm + 1) * sb.nomCBlkH - (sb.ulcy - acb0y)
0491: + sb.uly - cblk.uly;
0492: } else { // Bottom-most code-block, ends where subband ends
0493: cblk.h = sb.uly + sb.h - cblk.uly;
0494: }
0495: cblk.wmseScaling = 1f;
0496:
0497: // Since we are in getNextInternCodeBlock() we can return a
0498: // reference to the internal buffer, no need to copy. Just initialize
0499: // the 'offset' and 'scanw'
0500: cblk.offset = cblk.uly * decomposedComps[c].w + cblk.ulx;
0501: cblk.scanw = decomposedComps[c].w;
0502:
0503: // For the data just put a reference to our buffer
0504: cblk.setData(decomposedComps[c].getData());
0505: // Return code-block
0506: return cblk;
0507: }
0508:
0509: /**
0510: * Returns the next code-block in the current tile for the specified
0511: * component, as a copy (see below). The order in which code-blocks are
0512: * returned is not specified. However each code-block is returned only
0513: * once and all code-blocks will be returned if the method is called 'N'
0514: * times, where 'N' is the number of code-blocks in the tile. After all
0515: * the code-blocks have been returned for the current tile calls to this
0516: * method will return 'null'.
0517: *
0518: * <P>When changing the current tile (through 'setTile()' or 'nextTile()')
0519: * this method will always return the first code-block, as if this method
0520: * was never called before for the new current tile.
0521: *
0522: * <P>The data returned by this method is always a copy of the internal
0523: * data of this object, and it can be modified "in place" without
0524: * any problems after being returned. The 'offset' of the returned data is
0525: * 0, and the 'scanw' is the same as the code-block width. The 'magbits'
0526: * of the returned data is not set by this method and should be
0527: * ignored. See the 'CBlkWTData' class.
0528: *
0529: * <P>The 'ulx' and 'uly' members of the returned 'CBlkWTData' object
0530: * contain the coordinates of the top-left corner of the block, with
0531: * respect to the tile, not the subband.
0532: *
0533: * @param c The component for which to return the next code-block.
0534: *
0535: * @param cblk If non-null this object will be used to return the new
0536: * code-block. If null a new one will be allocated and returned. If the
0537: * "data" array of the object is non-null it will be reused, if possible,
0538: * to return the data.
0539: *
0540: * @return The next code-block in the current tile for component 'c', or
0541: * null if all code-blocks for the current tile have been returned.
0542: *
0543: * @see CBlkWTData
0544: * */
0545: public CBlkWTData getNextCodeBlock(int c, CBlkWTData cblk) {
0546: // We can not directly use getNextInternCodeBlock() since that returns
0547: // a reference to the internal buffer, we have to copy that data
0548:
0549: int j, k;
0550: int w;
0551: Object dst_data; // a int[] or float[] object
0552: int[] dst_data_int;
0553: float[] dst_data_float;
0554: Object src_data; // a int[] or float[] object
0555:
0556: intData = (filters.getWTDataType(tIdx, c) == DataBlk.TYPE_INT);
0557:
0558: dst_data = null;
0559:
0560: // Cache the data array, if any
0561: if (cblk != null) {
0562: dst_data = cblk.getData();
0563: }
0564:
0565: // Get the next code-block
0566: cblk = getNextInternCodeBlock(c, cblk);
0567:
0568: if (cblk == null) {
0569: return null; // No more code-blocks in current tile for component
0570: // c
0571: }
0572:
0573: // Ensure size of output buffer
0574: if (intData) { // int data
0575: dst_data_int = (int[]) dst_data;
0576: if (dst_data_int == null
0577: || dst_data_int.length < cblk.w * cblk.h) {
0578: dst_data = new int[cblk.w * cblk.h];
0579: }
0580: } else { // float data
0581: dst_data_float = (float[]) dst_data;
0582: if (dst_data_float == null
0583: || dst_data_float.length < cblk.w * cblk.h) {
0584: dst_data = new float[cblk.w * cblk.h];
0585: }
0586: }
0587:
0588: // Copy data line by line
0589: src_data = cblk.getData();
0590: w = cblk.w;
0591: for (j = w * (cblk.h - 1), k = cblk.offset + (cblk.h - 1)
0592: * cblk.scanw; j >= 0; j -= w, k -= cblk.scanw) {
0593: System.arraycopy(src_data, k, dst_data, j, w);
0594: }
0595: cblk.setData(dst_data);
0596: cblk.offset = 0;
0597: cblk.scanw = w;
0598:
0599: return cblk;
0600: }
0601:
0602: /**
0603: * Return the data type of this CBlkWTDataSrc. Its value should be either
0604: * DataBlk.TYPE_INT or DataBlk.TYPE_FLOAT but can change according to the
0605: * current tile-component.
0606: *
0607: * @param t The index of the tile for which to return the data type.
0608: *
0609: * @param c The index of the component for which to return the data type.
0610: *
0611: * @return Current data type
0612: * */
0613: public int getDataType(int t, int c) {
0614: return filters.getWTDataType(t, c);
0615: }
0616:
0617: /**
0618: * Returns the next subband that will be used to get the next code-block
0619: * to return by the getNext[Intern]CodeBlock method.
0620: *
0621: * @param c The component
0622: *
0623: * @return Its returns the next subband that will be used to get the next
0624: * code-block to return by the getNext[Intern]CodeBlock method.
0625: **/
0626: private SubbandAn getNextSubband(int c) {
0627: int down = 1;
0628: int up = 0;
0629: int direction = down;
0630: SubbandAn nextsb;
0631:
0632: nextsb = currentSubband[c];
0633: //If it is the first call to this method
0634: if (nextsb == null) {
0635: nextsb = this .getAnSubbandTree(tIdx, c);
0636: //If there is no decomposition level then send the whole image
0637: if (!nextsb.isNode) {
0638: return nextsb;
0639: }
0640: }
0641:
0642: //Find the next subband to send
0643: do {
0644: //If the current subband is null then break
0645: if (nextsb == null) {
0646: break;
0647: }
0648:
0649: //If the current subband is a leaf then select the next leaf to
0650: //send or go up in the decomposition tree if the leaf was a LL
0651: //one.
0652: else if (!nextsb.isNode) {
0653: switch (nextsb.orientation) {
0654: case Subband.WT_ORIENT_HH:
0655: nextsb = (SubbandAn) nextsb.getParent().getLH();
0656: direction = down;
0657: break;
0658: case Subband.WT_ORIENT_LH:
0659: nextsb = (SubbandAn) nextsb.getParent().getHL();
0660: direction = down;
0661: break;
0662: case Subband.WT_ORIENT_HL:
0663: nextsb = (SubbandAn) nextsb.getParent().getLL();
0664: direction = down;
0665: break;
0666: case Subband.WT_ORIENT_LL:
0667: nextsb = (SubbandAn) nextsb.getParent();
0668: direction = up;
0669: break;
0670: }
0671: }
0672:
0673: //Else if the current subband is a node
0674: else if (nextsb.isNode) {
0675: //If the direction is down the select the HH subband of the
0676: //current node.
0677: if (direction == down) {
0678: nextsb = (SubbandAn) nextsb.getHH();
0679: }
0680: //Else the direction is up the select the next node to cover
0681: //or still go up in the decomposition tree if the node is a LL
0682: //subband
0683: else if (direction == up) {
0684: switch (nextsb.orientation) {
0685: case Subband.WT_ORIENT_HH:
0686: nextsb = (SubbandAn) nextsb.getParent().getLH();
0687: direction = down;
0688: break;
0689: case Subband.WT_ORIENT_LH:
0690: nextsb = (SubbandAn) nextsb.getParent().getHL();
0691: direction = down;
0692: break;
0693: case Subband.WT_ORIENT_HL:
0694: nextsb = (SubbandAn) nextsb.getParent().getLL();
0695: direction = down;
0696: break;
0697: case Subband.WT_ORIENT_LL:
0698: nextsb = (SubbandAn) nextsb.getParent();
0699: direction = up;
0700: break;
0701: }
0702: }
0703: }
0704:
0705: if (nextsb == null) {
0706: break;
0707: }
0708: } while (nextsb.isNode);
0709: return nextsb;
0710: }
0711:
0712: /**
0713: * Performs the forward wavelet transform on the whole band. It
0714: * iteratively decomposes the subbands from the top node to the leaves.
0715: *
0716: * @param band The band containing the float data to decompose
0717: *
0718: * @param subband The structure containing the coordinates of the current
0719: * subband in the whole band to decompose.
0720: *
0721: * @param c The index of the current component to decompose
0722: * */
0723: private void waveletTreeDecomposition(DataBlk band,
0724: SubbandAn subband, int c) {
0725:
0726: //If the current subband is a leaf then nothing to be done (a leaf is
0727: //not decomposed).
0728: if (!subband.isNode)
0729: return;
0730:
0731: else {
0732: //Perform the 2D wavelet decomposition of the current subband
0733: wavelet2DDecomposition(band, (SubbandAn) subband, c);
0734:
0735: //Perform the decomposition of the four resulting subbands
0736: waveletTreeDecomposition(band, (SubbandAn) subband.getHH(),
0737: c);
0738: waveletTreeDecomposition(band, (SubbandAn) subband.getLH(),
0739: c);
0740: waveletTreeDecomposition(band, (SubbandAn) subband.getHL(),
0741: c);
0742: waveletTreeDecomposition(band, (SubbandAn) subband.getLL(),
0743: c);
0744: }
0745: }
0746:
0747: /**
0748: * Performs the 2D forward wavelet transform on a subband of the initial
0749: * band. This method will successively perform 1D filtering steps on all
0750: * lines and then all columns of the subband. In this class only filters
0751: * with floating point implementations can be used.
0752: *
0753: * @param band The band containing the float data to decompose
0754: *
0755: * @param subband The structure containing the coordinates of the subband
0756: * in the whole band to decompose.
0757: *
0758: * @param c The index of the current component to decompose
0759: * */
0760: private void wavelet2DDecomposition(DataBlk band,
0761: SubbandAn subband, int c) {
0762:
0763: int ulx, uly, w, h;
0764: int band_w, band_h;
0765:
0766: // If subband is empty (i.e. zero size) nothing to do
0767: if (subband.w == 0 || subband.h == 0) {
0768: return;
0769: }
0770:
0771: ulx = subband.ulx;
0772: uly = subband.uly;
0773: w = subband.w;
0774: h = subband.h;
0775: band_w = getTileCompWidth(tIdx, c);
0776: band_h = getTileCompHeight(tIdx, c);
0777:
0778: if (intData) {
0779: //Perform the decompositions if the filter is implemented with an
0780: //integer arithmetic.
0781: int i, j;
0782: int offset;
0783: int[] tmpVector = new int[java.lang.Math.max(w, h)];
0784:
0785: int[] data = ((DataBlkInt) band).getDataInt();
0786:
0787: //Perform the vertical decomposition
0788: if (subband.ulcy % 2 == 0) { // Even start index => use LPF
0789: for (j = 0; j < w; j++) {
0790: offset = uly * band_w + ulx + j;
0791: for (i = 0; i < h; i++)
0792: tmpVector[i] = data[offset + (i * band_w)];
0793: subband.vFilter.analyze_lpf(tmpVector, 0, h, 1,
0794: data, offset, band_w, data, offset
0795: + ((h + 1) / 2) * band_w, band_w);
0796: }
0797: } else { // Odd start index => use HPF
0798: for (j = 0; j < w; j++) {
0799: offset = uly * band_w + ulx + j;
0800: for (i = 0; i < h; i++)
0801: tmpVector[i] = data[offset + (i * band_w)];
0802: subband.vFilter.analyze_hpf(tmpVector, 0, h, 1,
0803: data, offset, band_w, data, offset
0804: + (h / 2) * band_w, band_w);
0805: }
0806: }
0807:
0808: //Perform the horizontal decomposition.
0809: if (subband.ulcx % 2 == 0) { // Even start index => use LPF
0810: for (i = 0; i < h; i++) {
0811: offset = (uly + i) * band_w + ulx;
0812: for (j = 0; j < w; j++)
0813: tmpVector[j] = data[offset + j];
0814: subband.hFilter.analyze_lpf(tmpVector, 0, w, 1,
0815: data, offset, 1, data,
0816: offset + (w + 1) / 2, 1);
0817: }
0818: } else { // Odd start index => use HPF
0819: for (i = 0; i < h; i++) {
0820: offset = (uly + i) * band_w + ulx;
0821: for (j = 0; j < w; j++)
0822: tmpVector[j] = data[offset + j];
0823: subband.hFilter.analyze_hpf(tmpVector, 0, w, 1,
0824: data, offset, 1, data, offset + w / 2, 1);
0825: }
0826: }
0827: } else {
0828: //Perform the decompositions if the filter is implemented with a
0829: //float arithmetic.
0830: int i, j;
0831: int offset;
0832: float[] tmpVector = new float[java.lang.Math.max(w, h)];
0833: float[] data = ((DataBlkFloat) band).getDataFloat();
0834:
0835: //Perform the vertical decomposition.
0836: if (subband.ulcy % 2 == 0) { // Even start index => use LPF
0837: for (j = 0; j < w; j++) {
0838: offset = uly * band_w + ulx + j;
0839: for (i = 0; i < h; i++)
0840: tmpVector[i] = data[offset + (i * band_w)];
0841: subband.vFilter.analyze_lpf(tmpVector, 0, h, 1,
0842: data, offset, band_w, data, offset
0843: + ((h + 1) / 2) * band_w, band_w);
0844: }
0845: } else { // Odd start index => use HPF
0846: for (j = 0; j < w; j++) {
0847: offset = uly * band_w + ulx + j;
0848: for (i = 0; i < h; i++)
0849: tmpVector[i] = data[offset + (i * band_w)];
0850: subband.vFilter.analyze_hpf(tmpVector, 0, h, 1,
0851: data, offset, band_w, data, offset
0852: + (h / 2) * band_w, band_w);
0853: }
0854: }
0855: //Perform the horizontal decomposition.
0856: if (subband.ulcx % 2 == 0) { // Even start index => use LPF
0857: for (i = 0; i < h; i++) {
0858: offset = (uly + i) * band_w + ulx;
0859: for (j = 0; j < w; j++)
0860: tmpVector[j] = data[offset + j];
0861: subband.hFilter.analyze_lpf(tmpVector, 0, w, 1,
0862: data, offset, 1, data,
0863: offset + (w + 1) / 2, 1);
0864: }
0865: } else { // Odd start index => use HPF
0866: for (i = 0; i < h; i++) {
0867: offset = (uly + i) * band_w + ulx;
0868: for (j = 0; j < w; j++)
0869: tmpVector[j] = data[offset + j];
0870: subband.hFilter.analyze_hpf(tmpVector, 0, w, 1,
0871: data, offset, 1, data, offset + w / 2, 1);
0872: }
0873: }
0874: }
0875: }
0876:
0877: /**
0878: * Changes the current tile, given the new coordinates.
0879: *
0880: * <P>This method resets the 'subbTrees' array, and recalculates the
0881: * values of the 'reversible' array. It also resets the decomposed
0882: * component buffers.
0883: *
0884: * @param x The horizontal coordinate of the tile.
0885: *
0886: * @param y The vertical coordinate of the new tile.
0887: * */
0888: public void setTile(int x, int y) {
0889: int i;
0890:
0891: // Change tile
0892: super .setTile(x, y);
0893:
0894: // Reset the decomposed component buffers.
0895: if (decomposedComps != null) {
0896: for (i = decomposedComps.length - 1; i >= 0; i--) {
0897: decomposedComps[i] = null;
0898: currentSubband[i] = null;
0899: }
0900: }
0901:
0902: }
0903:
0904: /**
0905: * Advances to the next tile, in standard scan-line order (by rows then
0906: * columns). An NoNextElementException is thrown if the current tile is
0907: * the last one (i.e. there is no next tile).
0908: *
0909: * <P>This method resets the 'subbTrees' array, and recalculates the
0910: * values of the 'reversible' array. It also resets the decomposed
0911: * component buffers.
0912: * */
0913: public void nextTile() {
0914: int i;
0915:
0916: // Change tile
0917: super .nextTile();
0918: // Reset the decomposed component buffers
0919: if (decomposedComps != null) {
0920: for (i = decomposedComps.length - 1; i >= 0; i--) {
0921: decomposedComps[i] = null;
0922: currentSubband[i] = null;
0923: }
0924: }
0925:
0926: }
0927:
0928: /**
0929: * Returns a reference to the subband tree structure representing the
0930: * subband decomposition for the specified tile-component of the source.
0931: *
0932: * @param t The index of the tile.
0933: *
0934: * @param c The index of the component.
0935: *
0936: * @return The subband tree structure, see Subband.
0937: *
0938: * @see SubbandAn
0939: * @see Subband
0940: * */
0941: public SubbandAn getAnSubbandTree(int t, int c) {
0942: if (subbTrees[t][c] == null) {
0943: subbTrees[t][c] = new SubbandAn(getTileCompWidth(tIdx, c),
0944: getTileCompHeight(tIdx, c), getCompULX(c),
0945: getCompULY(c), getDecompLevels(t, c),
0946: getHorAnWaveletFilters(t, c),
0947: getVertAnWaveletFilters(t, c));
0948: initSubbandsFields(t, c, subbTrees[t][c]);
0949: }
0950: return subbTrees[t][c];
0951: }
0952:
0953: /**
0954: * Initialises subbands fields, such as number of code-blocks and
0955: * code-blocks dimension, in the subband tree. The nominal code-block
0956: * width/height depends on the precincts dimensions if used.
0957: *
0958: * @param t The tile index of the subband
0959: *
0960: * @param c The component index
0961: *
0962: * @param sb The subband tree to be initialised.
0963: * */
0964: private void initSubbandsFields(int t, int c, Subband sb) {
0965: int cbw = cblks.getCBlkWidth(ModuleSpec.SPEC_TILE_COMP, t, c);
0966: int cbh = cblks.getCBlkHeight(ModuleSpec.SPEC_TILE_COMP, t, c);
0967:
0968: if (!sb.isNode) {
0969: // Code-blocks dimension
0970: int ppx, ppy;
0971: int ppxExp, ppyExp, cbwExp, cbhExp;
0972: ppx = pss.getPPX(t, c, sb.resLvl);
0973: ppy = pss.getPPY(t, c, sb.resLvl);
0974:
0975: if (ppx != Markers.PRECINCT_PARTITION_DEF_SIZE
0976: || ppy != Markers.PRECINCT_PARTITION_DEF_SIZE) {
0977:
0978: ppxExp = MathUtil.log2(ppx);
0979: ppyExp = MathUtil.log2(ppy);
0980: cbwExp = MathUtil.log2(cbw);
0981: cbhExp = MathUtil.log2(cbh);
0982:
0983: // Precinct partition is used
0984: switch (sb.resLvl) {
0985: case 0:
0986: sb.nomCBlkW = (cbwExp < ppxExp ? (1 << cbwExp)
0987: : (1 << ppxExp));
0988: sb.nomCBlkH = (cbhExp < ppyExp ? (1 << cbhExp)
0989: : (1 << ppyExp));
0990: break;
0991:
0992: default:
0993: sb.nomCBlkW = (cbwExp < ppxExp - 1 ? (1 << cbwExp)
0994: : (1 << (ppxExp - 1)));
0995: sb.nomCBlkH = (cbhExp < ppyExp - 1 ? (1 << cbhExp)
0996: : (1 << (ppyExp - 1)));
0997: break;
0998: }
0999: } else {
1000: sb.nomCBlkW = cbw;
1001: sb.nomCBlkH = cbh;
1002: }
1003:
1004: // Number of code-blocks
1005: if (sb.numCb == null)
1006: sb.numCb = new Point();
1007: if (sb.w != 0 && sb.h != 0) {
1008: int acb0x = cb0x;
1009: int acb0y = cb0y;
1010: int tmp;
1011:
1012: // Project code-block partition origin to subband. Since the
1013: // origin is always 0 or 1, it projects to the low-pass side
1014: // (throught the ceil operator) as itself (i.e. no change) and
1015: // to the high-pass side (through the floor operator) as 0,
1016: // always.
1017: switch (sb.sbandIdx) {
1018: case Subband.WT_ORIENT_LL:
1019: // No need to project since all low-pass => nothing to do
1020: break;
1021: case Subband.WT_ORIENT_HL:
1022: acb0x = 0;
1023: break;
1024: case Subband.WT_ORIENT_LH:
1025: acb0y = 0;
1026: break;
1027: case Subband.WT_ORIENT_HH:
1028: acb0x = 0;
1029: acb0y = 0;
1030: break;
1031: default:
1032: throw new Error("Internal JJ2000 error");
1033: }
1034: if (sb.ulcx - acb0x < 0 || sb.ulcy - acb0y < 0) {
1035: throw new IllegalArgumentException(
1036: "Invalid code-blocks "
1037: + "partition origin or "
1038: + "image offset in the "
1039: + "reference grid.");
1040: }
1041: // NOTE: when calculating "floor()" by integer division the
1042: // dividend and divisor must be positive, we ensure that by
1043: // adding the divisor to the dividend and then substracting 1
1044: // to the result of the division
1045: tmp = sb.ulcx - acb0x + sb.nomCBlkW;
1046: sb.numCb.x = (tmp + sb.w - 1) / sb.nomCBlkW
1047: - (tmp / sb.nomCBlkW - 1);
1048: tmp = sb.ulcy - acb0y + sb.nomCBlkH;
1049: sb.numCb.y = (tmp + sb.h - 1) / sb.nomCBlkH
1050: - (tmp / sb.nomCBlkH - 1);
1051: } else {
1052: sb.numCb.x = sb.numCb.y = 0;
1053: }
1054: } else {
1055: initSubbandsFields(t, c, sb.getLL());
1056: initSubbandsFields(t, c, sb.getHL());
1057: initSubbandsFields(t, c, sb.getLH());
1058: initSubbandsFields(t, c, sb.getHH());
1059: }
1060: }
1061:
1062: }
|