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