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