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: * */
044: package jj2000.j2k.roi.encoder;
045:
046: import jj2000.j2k.quantization.quantizer.*;
047: import jj2000.j2k.codestream.writer.*;
048: import jj2000.j2k.wavelet.analysis.*;
049: import jj2000.j2k.quantization.*;
050: import jj2000.j2k.image.input.*;
051: import jj2000.j2k.wavelet.*;
052: import jj2000.j2k.image.*;
053: import jj2000.j2k.util.*;
054: import 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: * */
070: public 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,
127: int c) {
128: int x = db.ulx;
129: int y = db.uly;
130: int w = db.w;
131: int h = db.h;
132: int tilew = sb.w;
133: int tileh = sb.h;
134: int[] maskData = (int[]) db.getData();
135: int i, j, k, bi, wrap;
136:
137: // If the ROI mask has not been calculated for this tile and
138: // component, do so now.
139: if (!tileMaskMade[c]) {
140: makeMask(sb, magbits, c);
141: tileMaskMade[c] = true;
142: }
143: if (!roiInTile)
144: return false;
145:
146: int[] mask = roiMask[c]; // local copy
147:
148: // Copy relevant part of the ROI mask to the datablock
149: i = (y + h - 1) * tilew + x + w - 1;
150: bi = w * h - 1;
151: wrap = tilew - w;
152: for (j = h; j > 0; j--) {
153: for (k = w; k > 0; k--, i--, bi--) {
154: maskData[bi] = mask[i];
155: }
156: i -= wrap;
157: }
158: return true;
159:
160: }
161:
162: /**
163: * This function returns the relevant data of the mask generator
164: * */
165: public String toString() {
166: return ("Fast rectangular ROI mask generator");
167: }
168:
169: /**
170: * This function generates the ROI mask for one tile-component.
171: *
172: * <P> Once the mask is generated in the pixel domain. it is decomposed
173: * following the same decomposition scheme as the wavelet transform.
174: *
175: * @param sb The root of the subband tree used in the decomposition
176: *
177: * @param magbits The max number of magnitude bits in any code-block
178: *
179: * @param c component number
180: */
181: public void makeMask(Subband sb, int magbits, int c) {
182: int mask[]; // local copy
183: ROI rois[] = this .rois; // local copy
184: int i, j, k, r, mink, minj, maxj;
185: int lrx, lry;
186: int x, y, w, h;
187: int cx, cy, rad;
188: int wrap;
189: int curScalVal;
190: int tileulx = sb.ulcx;
191: int tileuly = sb.ulcy;
192: int tilew = sb.w;
193: int tileh = sb.h;
194: int lineLen = (tilew > tileh) ? tilew : tileh;
195:
196: // Make sure there is a sufficiently large mask buffer
197: if (roiMask[c] == null || (roiMask[c].length < (tilew * tileh))) {
198: roiMask[c] = new int[tilew * tileh];
199: mask = roiMask[c];
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
208: || (maskLineLow.length < (lineLen + 1) / 2))
209: maskLineLow = new int[(lineLen + 1) / 2];
210: if (maskLineHigh == null
211: || (maskLineHigh.length < (lineLen + 1) / 2))
212: maskLineHigh = new int[(lineLen + 1) / 2];
213:
214: roiInTile = false;
215: // Generate ROIs in pixel domain:
216: for (r = rois.length - 1; r >= 0; r--) {
217: if (rois[r].comp == c) {
218: curScalVal = magbits;
219:
220: if (rois[r].arbShape) {
221: ImgReaderPGM maskPGM = rois[r].maskPGM; // Local copy
222:
223: if ((src.getImgWidth() != maskPGM.getImgWidth())
224: || (src.getImgHeight() != maskPGM
225: .getImgHeight()))
226: throw new IllegalArgumentException(
227: "Input image and" + " ROI mask must "
228: + "have the same " + "size");
229: x = src.getImgULX();
230: y = src.getImgULY();
231: lrx = x + src.getImgWidth() - 1;
232: lry = y + src.getImgHeight() - 1;
233: if ((x > tileulx + tilew) || (y > tileuly + tileh)
234: || (lrx < tileulx) || (lry < tileuly)) // Roi not in tile
235: continue;
236:
237: // Check bounds
238: x -= tileulx;
239: lrx -= tileulx;
240: y -= tileuly;
241: lry -= tileuly;
242:
243: int offx = 0;
244: int offy = 0;
245: if (x < 0) {
246: offx = -x;
247: x = 0;
248: }
249: if (y < 0) {
250: offy = -y;
251: y = 0;
252: }
253: w = (lrx > (tilew - 1)) ? tilew - x : lrx + 1 - x;
254: h = (lry > (tileh - 1)) ? tileh - y : lry + 1 - y;
255:
256: // Get shape line by line to reduce memory
257: DataBlkInt srcblk = new DataBlkInt();
258: int mDcOff = -ImgReaderPGM.DC_OFFSET;
259: int nROIcoeff = 0;
260: int[] src_data;
261: srcblk.ulx = offx;
262: srcblk.w = w;
263: srcblk.h = 1;
264:
265: i = (y + h - 1) * tilew + x + w - 1;
266: maxj = w;
267: wrap = tilew - maxj;
268: for (k = h; k > 0; k--) {
269: srcblk.uly = offy + k - 1;
270: srcblk = (DataBlkInt) maskPGM
271: .getInternCompData(srcblk, 0);
272: src_data = srcblk.getDataInt();
273:
274: for (j = maxj; j > 0; j--, i--) {
275: if (src_data[j - 1] != mDcOff) {
276: mask[i] = curScalVal;
277: nROIcoeff++;
278: }
279: }
280: i -= wrap;
281: }
282:
283: if (nROIcoeff != 0) {
284: roiInTile = true;
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: } else { // Non-rectangular ROI. So far only circular case
319: cx = rois[r].x - tileulx;
320: cy = rois[r].y - tileuly;
321: rad = rois[r].r;
322: i = tileh * tilew - 1;
323: for (k = tileh - 1; k >= 0; k--) {
324: for (j = tilew - 1; j >= 0; j--, i--) {
325: if (((j - cx) * (j - cx) + (k - cy)
326: * (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 = vFilter.getSynLowNegSupport()
343: + vFilter.getSynLowPosSupport();
344: int hvsup = vFilter.getSynHighNegSupport()
345: + vFilter.getSynHighPosSupport();
346: int lhsup = hFilter.getSynLowNegSupport()
347: + hFilter.getSynLowPosSupport();
348: int hhsup = hFilter.getSynHighNegSupport()
349: + 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: if (!sb.isNode)
391: return;
392:
393: // HORIZONTAL DECOMPOSITION
394:
395: // Calculate number of high and low samples after decomposition
396: // and get support for low and high filters
397: WaveletFilter filter = sb.getHorWFilter();
398: int lnSup = filter.getSynLowNegSupport();
399: int hnSup = filter.getSynHighNegSupport();
400: int lpSup = filter.getSynLowPosSupport();
401: int hpSup = filter.getSynHighPosSupport();
402: int lsup = lnSup + lpSup + 1;
403: int hsup = hnSup + hpSup + 1;
404:
405: // Calculate number of high/low coeffis in subbands
406: highFirst = sb.ulcx % 2;
407: if (sb.w % 2 == 0) {
408: lmax = w / 2 - 1;
409: hmax = lmax;
410: } else {
411: if (highFirst == 0) {
412: lmax = (w + 1) / 2 - 1;
413: hmax = w / 2 - 1;
414: } else {
415: hmax = (w + 1) / 2 - 1;
416: lmax = w / 2 - 1;
417: }
418: }
419:
420: int maxnSup = (lnSup > hnSup) ? lnSup : hnSup; // Maximum negative support
421: int maxpSup = (lpSup > hpSup) ? lpSup : hpSup; // Maximum positive support
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: } else {
489: if (sb.ulcy % 2 == 0) {
490: lmax = (h + 1) / 2 - 1;
491: hmax = h / 2 - 1;
492: } else {
493: hmax = (h + 1) / 2 - 1;
494: lmax = h / 2 - 1;
495: }
496: }
497:
498: maxnSup = (lnSup > hnSup) ? lnSup : hnSup; // Maximum negative support
499: maxpSup = (lpSup > hpSup) ? lpSup : hpSup; // Maximum positive support
500:
501: // Set padding to 0
502: for (pin = maxnSup - 1; pin >= 0; pin--)
503: padLine[pin] = 0;
504: for (pin = maxnSup + h - 1 + maxpSup; pin >= h; pin--)
505: padLine[pin] = 0;
506:
507: // Do decomposition of all columns
508: lineoffs = (uly + h - 1) * tilew + ulx + w;
509: for (j = w - 1; j >= 0; j--) {
510: lineoffs--;
511: // Get the line to transform from the mask
512: mi = lineoffs;
513: for (k = h, pin = k - 1 + maxnSup; k > 0; k--, mi -= tilew, pin--) {
514: padLine[pin] = mask[mi];
515: }
516: lastpin = maxnSup + highFirst + 2 * lmax + lpSup;
517: for (k = lmax; k >= 0; k--, lastpin -= 2) { // Low frequency samples
518: pin = lastpin;
519: for (s = lsup; s > 0; s--, pin--) {
520: scalVal = padLine[pin];
521: if (scalVal > maxVal)
522: maxVal = scalVal;
523: }
524: low[k] = maxVal;
525: maxVal = 0;
526: }
527: lastpin = maxnSup - highFirst + 2 * hmax + 1 + hpSup;
528: for (k = hmax; k >= 0; k--, lastpin -= 2) { // High frequency samples
529: pin = lastpin;
530: for (s = hsup; s > 0; s--, pin--) {
531: scalVal = padLine[pin];
532: if (scalVal > maxVal)
533: maxVal = scalVal;
534: }
535: high[k] = maxVal;
536: maxVal = 0;
537: }
538: // Put the lows and highs back
539: mi = lineoffs;
540: for (k = hmax; k >= 0; k--, mi -= tilew) {
541: mask[mi] = high[k];
542: }
543: for (k = lmax; k >= 0; k--, mi -= tilew) {
544: mask[mi] = low[k];
545: }
546: }
547:
548: if (sb.isNode) {
549: decomp(sb.getHH(), tilew, tileh, c);
550: decomp(sb.getLH(), tilew, tileh, c);
551: decomp(sb.getHL(), tilew, tileh, c);
552: decomp(sb.getLL(), tilew, tileh, c);
553: }
554:
555: }
556: }
|