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