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