001/* 002 * $RCSfile: ArbROIMaskGenerator.java,v $ 003 * $Revision: 1.1 $ 004 * $Date: 2005/02/11 05:02:22 $ 005 * $State: Exp $ 006 * 007 * Class: ArbROIMaskGenerator 008 * 009 * Description: Generates masks when only rectangular ROIs exist 010 * 011 * 012 * 013 * COPYRIGHT: 014 * 015 * This software module was originally developed by Raphaël Grosbois and 016 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel 017 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David 018 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research 019 * Centre France S.A) in the course of development of the JPEG2000 020 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This 021 * software module is an implementation of a part of the JPEG 2000 022 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio 023 * Systems AB and Canon Research Centre France S.A (collectively JJ2000 024 * Partners) agree not to assert against ISO/IEC and users of the JPEG 025 * 2000 Standard (Users) any of their rights under the copyright, not 026 * including other intellectual property rights, for this software module 027 * with respect to the usage by ISO/IEC and Users of this software module 028 * or modifications thereof for use in hardware or software products 029 * claiming conformance to the JPEG 2000 Standard. Those intending to use 030 * this software module in hardware or software products are advised that 031 * their use may infringe existing patents. The original developers of 032 * this software module, JJ2000 Partners and ISO/IEC assume no liability 033 * for use of this software module or modifications thereof. No license 034 * or right to this software module is granted for non JPEG 2000 Standard 035 * conforming products. JJ2000 Partners have full right to use this 036 * software module for his/her own purpose, assign or donate this 037 * software module to any third party and to inhibit third parties from 038 * using this software module for non JPEG 2000 Standard conforming 039 * products. This copyright notice must be included in all copies or 040 * derivative works of this software module. 041 * 042 * Copyright (c) 1999/2000 JJ2000 Partners. 043 * */ 044package jj2000.j2k.roi.encoder; 045 046import jj2000.j2k.image.DataBlkInt; 047import jj2000.j2k.image.input.ImgReaderPGM; 048import jj2000.j2k.quantization.quantizer.Quantizer; 049import jj2000.j2k.wavelet.Subband; 050import jj2000.j2k.wavelet.WaveletFilter; 051 052/** 053 * This class generates the ROI bit-mask when, at least, one ROI is not 054 * rectangular. In this case, the fast ROI bit-mask algorithm generation can 055 * not be used. 056 * 057 * <P>The values are calculated from the scaling factors of the ROIs. The 058 * values with which to scale are equal to u-umin where umin is the lowest 059 * scaling factor within the block. The umin value is sent to the entropy 060 * coder to be used for scaling the distortion values. 061 * 062 * @see ROIMaskGenerator 063 * 064 * @see ArbROIMaskGenerator 065 * */ 066public class ArbROIMaskGenerator extends ROIMaskGenerator{ 067 068 /** The source of quantized wavelet transform coefficients */ 069 private Quantizer src; 070 071 /** The ROI mask for the current tile for all components*/ 072 private int[][] roiMask; 073 074 /** The low frequency part of a mask line */ 075 private int[] maskLineLow; 076 077 /** The High frequency part of a mask line */ 078 private int[] maskLineHigh; 079 080 /** A line or column of the mask with padding */ 081 private int[] paddedMaskLine; 082 083 /** Flag indicating if any ROI was found to be in this tile */ 084 private boolean roiInTile; 085 086 /** 087 * The constructor of the arbitrary mask generator 088 * 089 * @param rois The ROI info. 090 * 091 * @param nrc The number of components 092 * 093 * @param src The quantizer module 094 * */ 095 public ArbROIMaskGenerator(ROI[] rois, int nrc, Quantizer src){ 096 super(rois,nrc); 097 roiMask=new int[nrc][]; 098 this.src = src; 099 } 100 101 /** 102 * This functions gets a DataBlk the size of the current code-block an 103 * fills this block with the ROI mask. 104 * 105 * <P> In order to get the mask for a particular Subband, the subband tree 106 * is traversed and at each decomposition, the ROI masks are computed. 107 * 108 * <P> The widths of the synthesis filters corresponding to the wavelet 109 * filters used in the wavelet transform are used to expand the ROI masks 110 * in the decompositions. 111 * 112 * @param db The data block that is to be filled with the mask 113 * 114 * @param sb The root of the subband tree to which db belongs 115 * 116 * @param magbits The max number of magnitude bits in any code-block 117 * 118 * @param c The number of the component 119 * 120 * @return Whether or not a mask was needed for this tile 121 **/ 122 public boolean getROIMask(DataBlkInt db, Subband sb, int magbits, int c){ 123 int x = db.ulx; 124 int y = db.uly; 125 int w = db.w; 126 int h = db.h; 127 int tilew = sb.w; 128 int tileh = sb.h; 129 int[] maskData= (int[])db.getData(); 130 int i, j, k, bi, wrap; 131 132 // If the ROI mask has not been calculated for this tile and 133 // component, do so now. 134 if(!tileMaskMade[c]){ 135 makeMask(sb,magbits,c); 136 tileMaskMade[c]=true; 137 } 138 if(!roiInTile) 139 return false; 140 141 int[] mask = roiMask[c]; // local copy 142 143 // Copy relevant part of the ROI mask to the datablock 144 i=(y+h-1)*tilew+x+w-1; 145 bi=w*h-1; 146 wrap=tilew-w; 147 for(j=h ; j>0 ; j--){ 148 for(k=w ; k>0 ; k--, i--, bi--){ 149 maskData[bi]=mask[i]; 150 } 151 i-=wrap; 152 } 153 return true; 154 155 } 156 157 /** 158 * This function returns the relevant data of the mask generator 159 * */ 160 public String toString(){ 161 return("Fast rectangular ROI mask generator"); 162 } 163 164 /** 165 * This function generates the ROI mask for one tile-component. 166 * 167 * <P> Once the mask is generated in the pixel domain. it is decomposed 168 * following the same decomposition scheme as the wavelet transform. 169 * 170 * @param sb The root of the subband tree used in the decomposition 171 * 172 * @param magbits The max number of magnitude bits in any code-block 173 * 174 * @param c component number 175 */ 176 public void makeMask(Subband sb, int magbits, int c){ 177 int mask[]; // local copy 178 ROI rois[] = this.rois; // local copy 179 int i,j,k,r,mink,minj,maxj; 180 int lrx,lry; 181 int x,y,w,h; 182 int cx,cy,rad; 183 int wrap; 184 int curScalVal; 185 int tileulx = sb.ulcx; 186 int tileuly = sb.ulcy; 187 int tilew = sb.w; 188 int tileh = sb.h; 189 int lineLen = (tilew>tileh) ? tilew : tileh; 190 191 // Make sure there is a sufficiently large mask buffer 192 if(roiMask[c] == null || ( roiMask[c].length < (tilew*tileh ))){ 193 roiMask[c] = new int[tilew*tileh]; 194 mask = roiMask[c]; 195 } 196 else{ 197 mask = roiMask[c]; 198 for(i=tilew*tileh-1; i>=0; i--) 199 mask[i] = 0; 200 } 201 202 // Make sure there are sufficiently large line buffers 203 if(maskLineLow == null || (maskLineLow.length < (lineLen+1)/2)) 204 maskLineLow = new int[(lineLen+1)/2]; 205 if(maskLineHigh == null || (maskLineHigh.length < (lineLen+1)/2)) 206 maskLineHigh = new int[(lineLen+1)/2]; 207 208 roiInTile = false; 209 // Generate ROIs in pixel domain: 210 for(r=rois.length-1; r>=0; r--) { 211 if(rois[r].comp == c) { 212 curScalVal = magbits; 213 214 if (rois[r].arbShape) { 215 ImgReaderPGM maskPGM = rois[r].maskPGM; // Local copy 216 217 if( (src.getImgWidth() != maskPGM.getImgWidth()) || 218 (src.getImgHeight() != maskPGM.getImgHeight()) ) 219 throw new IllegalArgumentException("Input image and"+ 220 " ROI mask must "+ 221 "have the same "+ 222 "size"); 223 x = src.getImgULX(); 224 y = src.getImgULY(); 225 lrx = x+src.getImgWidth()-1; 226 lry = y+src.getImgHeight()-1; 227 if( (x>tileulx+tilew) || (y>tileuly+tileh) || 228 (lrx<tileulx) || (lry<tileuly) ) // Roi not in tile 229 continue; 230 231 // Check bounds 232 x -= tileulx; 233 lrx -= tileulx; 234 y -= tileuly; 235 lry -= tileuly; 236 237 int offx = 0; 238 int offy = 0; 239 if(x<0) { 240 offx = -x; 241 x = 0; 242 } 243 if(y<0) { 244 offy = -y; 245 y = 0; 246 } 247 w = (lrx > (tilew-1))? tilew-x:lrx+1-x; 248 h = (lry > (tileh-1))? tileh-y:lry+1-y; 249 250 251 // Get shape line by line to reduce memory 252 DataBlkInt srcblk = new DataBlkInt(); 253 int mDcOff = -ImgReaderPGM.DC_OFFSET; 254 int nROIcoeff = 0; 255 int[] src_data; 256 srcblk.ulx = offx; 257 srcblk.w = w; 258 srcblk.h = 1; 259 260 i = (y+h-1)*tilew+x+w-1; 261 maxj = w; 262 wrap = tilew-maxj; 263 for(k=h; k>0; k--){ 264 srcblk.uly = offy+k-1; 265 srcblk = (DataBlkInt)maskPGM. 266 getInternCompData(srcblk,0); 267 src_data = srcblk.getDataInt(); 268 269 for(j=maxj; j>0; j--,i--){ 270 if(src_data[j-1] != mDcOff) { 271 mask[i] = curScalVal; 272 nROIcoeff++; 273 } 274 } 275 i -= wrap; 276 } 277 278 if(nROIcoeff != 0) { 279 roiInTile = true; 280 } 281 } 282 else if(rois[r].rect){ // Rectangular ROI 283 x = rois[r].ulx; 284 y = rois[r].uly; 285 lrx = rois[r].w+x-1; 286 lry = rois[r].h+y-1; 287 288 if( (x>tileulx+tilew) || (y>tileuly+tileh) || 289 (lrx<tileulx) || (lry<tileuly) ) // Roi not in tile 290 continue; 291 292 roiInTile=true; 293 294 // Check bounds 295 x -= tileulx; 296 lrx -= tileulx; 297 y -= tileuly; 298 lry -= tileuly; 299 300 x = (x<0) ? 0:x; 301 y = (y<0) ? 0:y; 302 w = (lrx > (tilew-1))? tilew-x:lrx+1-x; 303 h = (lry > (tileh-1))? tileh-y:lry+1-y; 304 305 i = (y+h-1)*tilew+x+w-1; 306 maxj = w; 307 wrap = tilew-maxj; 308 for(k=h; k>0; k--){ 309 for(j=maxj; j>0; j--,i--){ 310 mask[i] = curScalVal; 311 } 312 i -= wrap; 313 } 314 } 315 else{ // Non-rectangular ROI. So far only circular case 316 cx = rois[r].x-tileulx; 317 cy = rois[r].y-tileuly; 318 rad = rois[r].r; 319 i = tileh*tilew-1; 320 for(k=tileh-1; k>=0; k--){ 321 for(j=tilew-1; j>=0; j--,i--){ 322 if(((j-cx)*(j-cx)+(k-cy)*(k-cy) < rad*rad)){ 323 mask[i] = curScalVal; 324 roiInTile = true; 325 } 326 } 327 } 328 } 329 } 330 } 331 332 // If wavelet transform is used 333 if(sb.isNode) { 334 // Decompose the mask according to the subband tree 335 // Calculate size of padded line buffer 336 WaveletFilter vFilter = sb.getVerWFilter(); 337 WaveletFilter hFilter = sb.getHorWFilter(); 338 int lvsup = 339 vFilter.getSynLowNegSupport()+vFilter.getSynLowPosSupport(); 340 int hvsup = 341 vFilter.getSynHighNegSupport()+vFilter.getSynHighPosSupport(); 342 int lhsup = 343 hFilter.getSynLowNegSupport()+hFilter.getSynLowPosSupport(); 344 int hhsup = 345 hFilter.getSynHighNegSupport()+hFilter.getSynHighPosSupport(); 346 lvsup = (lvsup>hvsup)? lvsup:hvsup; 347 lhsup = (lhsup>hhsup)? lhsup:hhsup; 348 lvsup = (lvsup>lhsup)? lvsup:lhsup; 349 paddedMaskLine = new int[lineLen+lvsup]; 350 351 if(roiInTile) 352 decomp(sb,tilew,tileh,c); 353 } 354 } 355 356 /** 357 * This function decomposes the mask for a node in the subband tree. 358 * after the mask is decomposed for a node, this function is called for 359 * the children of the subband. The decomposition is done line by line 360 * and column by column 361 * 362 * @param sb The subband that is to be used for the decomposition 363 * 364 * @param tilew The width of the current tile 365 * 366 * @param tileh The height of the current tile 367 * 368 * @param c component number 369 */ 370 private void decomp(Subband sb, int tilew, int tileh, int c){ 371 int ulx = sb.ulx; 372 int uly = sb.uly; 373 int w = sb.w; 374 int h = sb.h; 375 int scalVal,maxVal = 0; 376 int i,j,k,s,hi,mi = 0,pin,li; 377 int hmax,lmax,smax; 378 int wrap,lineoffs,lastlow; 379 int[] mask = roiMask[c]; // local copy 380 int[] low = maskLineLow; // local copy 381 int[] high = maskLineHigh; // local copy 382 int[] padLine = paddedMaskLine; // local copy 383 int highFirst = 0; 384 int lastpin; 385 386 387 if(!sb.isNode) 388 return; 389 390 // HORIZONTAL DECOMPOSITION 391 392 // Calculate number of high and low samples after decomposition 393 // and get support for low and high filters 394 WaveletFilter filter = sb.getHorWFilter(); 395 int lnSup = filter.getSynLowNegSupport(); 396 int hnSup = filter.getSynHighNegSupport(); 397 int lpSup = filter.getSynLowPosSupport(); 398 int hpSup = filter.getSynHighPosSupport(); 399 int lsup = lnSup+lpSup+1; 400 int hsup = hnSup+hpSup+1; 401 402 // Calculate number of high/low coeffis in subbands 403 highFirst = sb.ulcx%2; 404 if(sb.w%2==0){ 405 lmax = w/2-1; 406 hmax = lmax; 407 } 408 else{ 409 if(highFirst==0){ 410 lmax = (w+1)/2-1; 411 hmax = w/2-1; 412 } 413 else{ 414 hmax = (w+1)/2-1; 415 lmax = w/2-1; 416 } 417 } 418 419 int maxnSup = (lnSup>hnSup) ? lnSup:hnSup; // Maximum negative support 420 int maxpSup = (lpSup>hpSup) ? lpSup:hpSup; // Maximum positive support 421 422 423 // Set padding to 0 424 for(pin=maxnSup-1;pin>=0;pin--) 425 padLine[pin] = 0; 426 for(pin=maxnSup+w-1+maxpSup;pin>=w;pin--) 427 padLine[pin] = 0; 428 429 // Do decomposition of all lines 430 lineoffs = (uly+h)*tilew+ulx+w-1; 431 for(j=h-1;j>=0;j--){ 432 lineoffs -= tilew; 433 // Get the line to transform from the mask 434 mi=lineoffs; 435 for(k=w, pin=w-1+maxnSup ; k>0 ; k--,mi--,pin--){ 436 padLine[pin] = mask[mi]; 437 } 438 439 lastpin = maxnSup+highFirst+2*lmax+lpSup; 440 for(k=lmax; k>=0 ; k--,lastpin-=2){ // Low frequency samples 441 pin = lastpin; 442 for(s=lsup;s>0;s--,pin--){ 443 scalVal = padLine[pin]; 444 if(scalVal>maxVal) 445 maxVal = scalVal; 446 } 447 low[k] = maxVal; 448 maxVal = 0; 449 } 450 lastpin = maxnSup-highFirst+2*hmax+1+hpSup; 451 for(k=hmax; k>=0 ; k--,lastpin-=2){ // High frequency samples 452 pin = lastpin; 453 for(s=hsup;s>0;s--,pin--){ 454 scalVal = padLine[pin]; 455 if(scalVal>maxVal) 456 maxVal = scalVal; 457 } 458 high[k] = maxVal; 459 maxVal = 0; 460 } 461 // Put the lows and highs back 462 mi=lineoffs; 463 for(k=hmax; k>=0; k--,mi--){ 464 mask[mi] = high[k]; 465 } 466 for(k=lmax;k>=0;k--,mi--){ 467 mask[mi] = low[k]; 468 } 469 } 470 471 // VERTICAL DECOMPOSITION 472 473 // Calculate number of high and low samples after decomposition 474 // and get support for low and high filters 475 filter = sb.getVerWFilter(); 476 lnSup = filter.getSynLowNegSupport(); 477 hnSup = filter.getSynHighNegSupport(); 478 lpSup = filter.getSynLowPosSupport(); 479 hpSup = filter.getSynHighPosSupport(); 480 lsup = lnSup+lpSup+1; 481 hsup = hnSup+hpSup+1; 482 483 // Calculate number of high/low coeffs in subbands 484 highFirst = sb.ulcy%2; 485 if(sb.h%2==0){ 486 lmax = h/2-1; 487 hmax = lmax; 488 } 489 else{ 490 if(sb.ulcy%2==0){ 491 lmax = (h+1)/2-1; 492 hmax = h/2-1; 493 } 494 else{ 495 hmax = (h+1)/2-1; 496 lmax = h/2-1; 497 } 498 } 499 500 maxnSup = (lnSup>hnSup) ? lnSup:hnSup; // Maximum negative support 501 maxpSup = (lpSup>hpSup) ? lpSup:hpSup; // Maximum positive support 502 503 // Set padding to 0 504 for(pin=maxnSup-1;pin>=0;pin--) 505 padLine[pin] = 0; 506 for(pin=maxnSup+h-1+maxpSup;pin>=h;pin--) 507 padLine[pin] = 0; 508 509 // Do decomposition of all columns 510 lineoffs=(uly+h-1)*tilew+ulx+w; 511 for(j=w-1;j>=0;j--){ 512 lineoffs--; 513 // Get the line to transform from the mask 514 mi = lineoffs; 515 for(k=h, pin=k-1+maxnSup ; k>0 ; k--,mi-=tilew,pin--){ 516 padLine[pin] = mask[mi]; 517 } 518 lastpin=maxnSup+highFirst+2*lmax+lpSup; 519 for(k=lmax; k>=0 ; k--,lastpin-=2){ // Low frequency samples 520 pin = lastpin; 521 for(s=lsup;s>0;s--,pin--){ 522 scalVal = padLine[pin]; 523 if(scalVal>maxVal) 524 maxVal = scalVal; 525 } 526 low[k] = maxVal; 527 maxVal = 0; 528 } 529 lastpin = maxnSup-highFirst+2*hmax+1+hpSup; 530 for(k=hmax; k>=0 ; k--,lastpin-=2){ // High frequency samples 531 pin = lastpin; 532 for(s=hsup;s>0;s--,pin--){ 533 scalVal = padLine[pin]; 534 if(scalVal>maxVal) 535 maxVal = scalVal; 536 } 537 high[k] = maxVal; 538 maxVal = 0; 539 } 540 // Put the lows and highs back 541 mi=lineoffs; 542 for(k=hmax;k>=0;k--,mi-=tilew){ 543 mask[mi] = high[k]; 544 } 545 for(k=lmax;k>=0;k--,mi-=tilew){ 546 mask[mi] = low[k]; 547 } 548 } 549 550 if(sb.isNode){ 551 decomp(sb.getHH(), tilew, tileh, c); 552 decomp(sb.getLH(), tilew, tileh, c); 553 decomp(sb.getHL(), tilew, tileh, c); 554 decomp(sb.getLL(), tilew, tileh, c); 555 } 556 557 } 558} 559 560 561 562 563