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