= 0 means the lowest resolution level " + "belongs to the ROI, 1 means the two lowest etc. (-1 deactivates" + " the option)", "-1"}, new System.String[]{"Rno_rect", "[on|off]", "This argument makes sure that the ROI mask generation is not done " + "using the fast ROI mask generation for rectangular ROIs " + "regardless of whether the specified ROIs are rectangular or not", "off"}};
/// The maximum number of magnitude bit-planes in any subband. One value
/// for each tile-component
///
private int[][] maxMagBits;
/// Flag indicating the presence of ROIs
private bool roi;
/// Flag indicating if block aligned ROIs are used
private bool blockAligned;
/// Number of resolution levels to include in ROI mask
private int useStartLevel;
/// The class generating the ROI mask
private ROIMaskGenerator mg;
/// The ROI mask
private DataBlkInt roiMask;
/// The source of quantized wavelet transform coefficients
private Quantizer src;
/// Constructor of the ROI scaler, takes a Quantizer as source of data to
/// scale.
///
///
/// The quantizer that is the source of data.
///
///
/// The mask generator that will be used for all components
///
///
/// Flag indicating whether there are rois specified.
///
///
/// The resolution levels that belong entirely to ROI
///
///
/// Flag indicating whether block aligning is used.
///
///
/// The encoder specifications for addition of roi specs
///
///
public ROIScaler(Quantizer src, ROIMaskGenerator mg, bool roi, int sLev, bool uba, EncoderSpecs encSpec):base(src)
{
this.src = src;
this.roi = roi;
this.useStartLevel = sLev;
if (roi)
{
// If there is no ROI, no need to do this
this.mg = mg;
roiMask = new DataBlkInt();
calcMaxMagBits(encSpec);
blockAligned = uba;
}
}
/// Since ROI scaling is always a reversible operation, it calls
/// isReversible() method of it source (the quantizer module).
///
///
/// The tile to test for reversibility
///
///
/// The component to test for reversibility
///
///
/// True if the quantized data is reversible, false if not.
///
///
public virtual bool isReversible(int t, int c)
{
return src.isReversible(t, c);
}
/// Returns a reference to the subband tree structure representing the
/// subband decomposition for the specified tile-component.
///
///
/// The index of the tile.
///
///
/// The index of the component.
///
///
/// The subband tree structure, see SubbandAn.
///
///
///
///
///
///
///
///
public virtual SubbandAn getAnSubbandTree(int t, int c)
{
return src.getAnSubbandTree(t, c);
}
/// Creates a ROIScaler object. The Quantizer is the source of data to
/// scale.
///
/// The ROI Scaler creates a ROIMaskGenerator depending on what ROI
/// information is in the ParameterList. If only rectangular ROI are used,
/// the fast mask generator for rectangular ROI can be used.
///
///
/// The source of data to scale
///
///
/// The parameter list (or options).
///
///
/// The encoder specifications for addition of roi specs
///
///
/// If an error occurs while parsing
/// the options in 'pl'
///
///
public static ROIScaler createInstance(Quantizer src, ParameterList pl, EncoderSpecs encSpec)
{
System.Collections.ArrayList roiVector = System.Collections.ArrayList.Synchronized(new System.Collections.ArrayList(10));
ROIMaskGenerator maskGen = null;
// Check parameters
pl.checkList(OPT_PREFIX, CSJ2K.j2k.util.ParameterList.toNameArray(pinfo));
// Get parameters and check if there are and ROIs specified
System.String roiopt = pl.getParameter("Rroi");
if (roiopt == null)
{
// No ROIs specified! Create ROIScaler with no mask generator
return new ROIScaler(src, null, false, - 1, false, encSpec);
}
// Check if the lowest resolution levels should belong to the ROI
int sLev = pl.getIntParameter("Rstart_level");
// Check if the ROIs are block-aligned
bool useBlockAligned = pl.getBooleanParameter("Ralign");
// Check if generic mask generation is specified
bool onlyRect = !pl.getBooleanParameter("Rno_rect");
// Parse the ROIs
parseROIs(roiopt, src.NumComps, roiVector);
ROI[] roiArray = new ROI[roiVector.Count];
roiVector.CopyTo(roiArray);
// If onlyRect has been forced, check if there are any non-rectangular
// ROIs specified. Currently, only the presence of circular ROIs will
// make this false
if (onlyRect)
{
for (int i = roiArray.Length - 1; i >= 0; i--)
if (!roiArray[i].rect)
{
onlyRect = false;
break;
}
}
if (onlyRect)
{
// It's possible to use the fast ROI mask generation when only
// rectangular ROIs are specified.
maskGen = new RectROIMaskGenerator(roiArray, src.NumComps);
}
else
{
// It's necessary to use the generic mask generation
maskGen = new ArbROIMaskGenerator(roiArray, src.NumComps, src);
}
return new ROIScaler(src, maskGen, true, sLev, useBlockAligned, encSpec);
}
/// This function parses the values given for the ROIs with the argument
/// -Rroi. Currently only circular and rectangular ROIs are supported.
///
/// A rectangular ROI is indicated by a 'R' followed the coordinates for
/// the upper left corner of the ROI and then its width and height.
///
/// A circular ROI is indicated by a 'C' followed by the coordinates of
/// the circle center and then the radius.
///
/// Before the R and C values, the component that are affected by the
/// ROI are indicated.
///
///
/// The info on the ROIs
///
///
/// number of components
///
///
/// The vcector containing the ROI parsed from the cmd line
///
///
/// The ROIs specified in roiopt
///
///
protected internal static System.Collections.ArrayList parseROIs(System.String roiopt, int nc, System.Collections.ArrayList roiVector)
{
//ROI[] ROIs;
ROI roi;
SupportClass.Tokenizer stok;
//char tok;
int nrOfROIs = 0;
//char c;
int ulx, uly, w, h, x, y, rad; // comp removed
bool[] roiInComp = null;
stok = new SupportClass.Tokenizer(roiopt);
System.String word;
while (stok.HasMoreTokens())
{
word = stok.NextToken();
switch (word[0])
{
case 'c': // Components specification
roiInComp = ModuleSpec.parseIdx(word, nc);
break;
case 'R': // Rectangular ROI to be read
nrOfROIs++;
try
{
word = stok.NextToken();
ulx = (System.Int32.Parse(word));
word = stok.NextToken();
uly = (System.Int32.Parse(word));
word = stok.NextToken();
w = (System.Int32.Parse(word));
word = stok.NextToken();
h = (System.Int32.Parse(word));
}
catch (System.FormatException)
{
throw new System.ArgumentException("Bad parameter for " + "'-Rroi R' option : " + word);
}
catch (System.ArgumentOutOfRangeException)
{
throw new System.ArgumentException("Wrong number of " + "parameters for " + "h'-Rroi R' option.");
}
// If the ROI is component-specific, check which comps.
if (roiInComp != null)
for (int i = 0; i < nc; i++)
{
if (roiInComp[i])
{
roi = new ROI(i, ulx, uly, w, h);
roiVector.Add(roi);
}
}
else
{
// Otherwise add ROI for all components
for (int i = 0; i < nc; i++)
{
roi = new ROI(i, ulx, uly, w, h);
roiVector.Add(roi);
}
}
break;
case 'C': // Circular ROI to be read
nrOfROIs++;
try
{
word = stok.NextToken();
x = (System.Int32.Parse(word));
word = stok.NextToken();
y = (System.Int32.Parse(word));
word = stok.NextToken();
rad = (System.Int32.Parse(word));
}
catch (System.FormatException)
{
throw new System.ArgumentException("Bad parameter for " + "'-Rroi C' option : " + word);
}
catch (System.ArgumentOutOfRangeException)
{
throw new System.ArgumentException("Wrong number of " + "parameters for " + "'-Rroi C' option.");
}
// If the ROI is component-specific, check which comps.
if (roiInComp != null)
for (int i = 0; i < nc; i++)
{
if (roiInComp[i])
{
roi = new ROI(i, x, y, rad);
roiVector.Add(roi);
}
}
else
{
// Otherwise add ROI for all components
for (int i = 0; i < nc; i++)
{
roi = new ROI(i, x, y, rad);
roiVector.Add(roi);
}
}
break;
case 'A': // ROI wth arbitrary shape
nrOfROIs++;
System.String filename;
ImgReaderPGM maskPGM = null;
try
{
filename = stok.NextToken();
}
catch (System.ArgumentOutOfRangeException)
{
throw new System.ArgumentException("Wrong number of " + "parameters for " + "'-Rroi A' option.");
}
try
{
maskPGM = new ImgReaderPGM(filename);
}
catch (System.IO.IOException)
{
throw new System.ApplicationException("Cannot read PGM file with ROI");
}
// If the ROI is component-specific, check which comps.
if (roiInComp != null)
for (int i = 0; i < nc; i++)
{
if (roiInComp[i])
{
roi = new ROI(i, maskPGM);
roiVector.Add(roi);
}
}
else
{
// Otherwise add ROI for all components
for (int i = 0; i < nc; i++)
{
roi = new ROI(i, maskPGM);
roiVector.Add(roi);
}
}
break;
default:
throw new System.ApplicationException("Bad parameters for ROI nr " + roiVector.Count);
}
}
return roiVector;
}
/// This function gets a datablk from the entropy coder. The sample sin the
/// block, which consists of the quantized coefficients from the quantizer,
/// are scaled by the values given for any ROIs specified.
///
/// The function calls on a ROIMaskGenerator to get the mask for scaling
/// the coefficients in the current block.
///
/// The data returned by this method is a copy of the orignal
/// data. Therfore it can be modified "in place" without any problems after
/// being returned. The 'offset' of the returned data is 0, and the 'scanw'
/// is the same as the code-block width. See the 'CBlkWTData' class.
///
///
/// The component for which to return the next code-block.
///
///
/// If non-null this object will be used to return the new
/// code-block. If null a new one will be allocated and returned. If the
/// "data" array of the object is non-null it will be reused, if possible,
/// to return the data.
///
///
/// The next code-block in the current tile for component 'n', or
/// null if all code-blocks for the current tile have been returned.
///
///
///
///
///
public virtual CBlkWTData getNextCodeBlock(int c, CBlkWTData cblk)
{
return getNextInternCodeBlock(c, cblk);
}
/// This function gets a datablk from the entropy coder. The sample sin the
/// block, which consists of the quantized coefficients from the quantizer,
/// are scaled by the values given for any ROIs specified.
///
/// The function calls on a ROIMaskGenerator to get the mask for scaling
/// the coefficients in the current block.
///
///
/// The component for which to return the next code-block.
///
///
/// If non-null this object will be used to return the new
/// code-block. If null a new one will be allocated and returned. If the
/// "data" array of the object is non-null it will be reused, if possible,
/// to return the data.
///
///
/// The next code-block in the current tile for component 'n', or
/// null if all code-blocks for the current tile have been returned.
///
///
///
///
///
public virtual CBlkWTData getNextInternCodeBlock(int c, CBlkWTData cblk)
{
int mi, i, j, k, wrap;
int ulx, uly, w, h;
DataBlkInt mask = roiMask; // local copy of mask
int[] maskData; // local copy of mask data
int[] data; // local copy of quantized data
int tmp;
int bitMask = 0x7FFFFFFF;
SubbandAn root, sb;
int maxBits = 0; // local copy
bool roiInTile;
bool sbInMask;
int nROIcoeff = 0;
// Get codeblock's data from quantizer
cblk = src.getNextCodeBlock(c, cblk);
// If there is no ROI in the image, or if we already got all
// code-blocks
if (!roi || cblk == null)
{
return cblk;
}
data = (int[]) cblk.Data;
sb = cblk.sb;
ulx = cblk.ulx;
uly = cblk.uly;
w = cblk.w;
h = cblk.h;
sbInMask = (sb.resLvl <= useStartLevel);
// Check that there is an array for the mask and set it to zero
maskData = mask.DataInt; // local copy of mask data
if (maskData == null || w * h > maskData.Length)
{
maskData = new int[w * h];
mask.DataInt = maskData;
}
else
{
for (i = w * h - 1; i >= 0; i--)
maskData[i] = 0;
}
mask.ulx = ulx;
mask.uly = uly;
mask.w = w;
mask.h = h;
// Get ROI mask from generator
root = src.getAnSubbandTree(tIdx, c);
maxBits = maxMagBits[tIdx][c];
roiInTile = mg.getROIMask(mask, root, maxBits, c);
// If there is no ROI in this tile, return the code-block untouched
if (!roiInTile && (!sbInMask))
{
cblk.nROIbp = 0;
return cblk;
}
// Update field containing the number of ROI magnitude bit-planes
cblk.nROIbp = cblk.magbits;
// If the entire subband belongs to the ROI mask, The code-block is
// set to belong entirely to the ROI with the highest scaling value
if (sbInMask)
{
// Scale the wmse so that instead of scaling the coefficients, the
// wmse is scaled.
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
cblk.wmseScaling *= (float) (1 << (maxBits << 1));
cblk.nROIcoeff = w * h;
return cblk;
}
// In 'block aligned' mode, the code-block is set to belong entirely
// to the ROI with the highest scaling value if one coefficient, at
// least, belongs to the ROI
if (blockAligned)
{
wrap = cblk.scanw - w;
mi = h * w - 1;
i = cblk.offset + cblk.scanw * (h - 1) + w - 1;
int nroicoeff = 0;
for (j = h; j > 0; j--)
{
for (k = w - 1; k >= 0; k--, i--, mi--)
{
if (maskData[mi] != 0)
{
nroicoeff++;
}
}
i -= wrap;
}
if (nroicoeff != 0)
{
// Include the subband
//UPGRADE_WARNING: Data types in Visual C# might be different. Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
cblk.wmseScaling *= (float) (1 << (maxBits << 1));
cblk.nROIcoeff = w * h;
}
return cblk;
}
// Scale background coefficients
bitMask = (((1 << cblk.magbits) - 1) << (31 - cblk.magbits));
wrap = cblk.scanw - w;
mi = h * w - 1;
i = cblk.offset + cblk.scanw * (h - 1) + w - 1;
for (j = h; j > 0; j--)
{
for (k = w; k > 0; k--, i--, mi--)
{
tmp = data[i];
if (maskData[mi] != 0)
{
// ROI coeff. We need to erase fractional bits to ensure
// that they do not conflict with BG coeffs. This is only
// strictly necessary for ROI coeffs. which non-fractional
// magnitude is zero, but much better BG quality can be
// achieved if done if reset to zero since coding zeros is
// much more efficient (the entropy coder knows nothing
// about ROI and cannot avoid coding the ROI fractional
// bits, otherwise this would not be necessary).
data[i] = (unchecked((int) 0x80000000) & tmp) | (tmp & bitMask);
nROIcoeff++;
}
else
{
// BG coeff. it is not necessary to erase fractional bits
data[i] = (unchecked((int) 0x80000000) & tmp) | ((tmp & 0x7FFFFFFF) >> maxBits);
}
}
i -= wrap;
}
// Modify the number of significant bit-planes in the code-block
cblk.magbits += maxBits;
// Store the number of ROI coefficients present in the code-block
cblk.nROIcoeff = nROIcoeff;
return cblk;
}
/// This function returns the flag indicating if any ROI functionality used
///
///
/// Flag indicating whether there are ROIs in the image
///
public virtual bool useRoi()
{
return roi;
}
/// Changes the current tile, given the new indexes. An
/// IllegalArgumentException is thrown if the indexes do not
/// correspond to a valid tile.
///
///
/// The horizontal index of the tile.
///
///
/// The vertical index of the new tile.
///
///
public override void setTile(int x, int y)
{
base.setTile(x, y);
if (roi)
mg.tileChanged();
}
/// Advances to the next tile, in standard scan-line order (by rows then
/// columns). An NoNextElementException is thrown if the current tile is
/// the last one (i.e. there is no next tile).
///
///
public override void nextTile()
{
base.nextTile();
if (roi)
mg.tileChanged();
}
/// Calculates the maximum amount of magnitude bits for each
/// tile-component, and stores it in the 'maxMagBits' array. This is called
/// by the constructor
///
///
/// The encoder specifications for addition of roi specs
///
///
private void calcMaxMagBits(EncoderSpecs encSpec)
{
int tmp;
MaxShiftSpec rois = encSpec.rois;
int nt = src.getNumTiles();
int nc = src.NumComps;
maxMagBits = new int[nt][];
for (int i = 0; i < nt; i++)
{
maxMagBits[i] = new int[nc];
}
src.setTile(0, 0);
for (int t = 0; t < nt; t++)
{
for (int c = nc - 1; c >= 0; c--)
{
tmp = src.getMaxMagBits(c);
maxMagBits[t][c] = tmp;
rois.setTileCompVal(t, c, (System.Object) tmp);
}
if (t < nt - 1)
src.nextTile();
}
// Reset to current initial tile position
src.setTile(0, 0);
}
}
}