/* * CVS Identifier: * * $Id: ForwCompTransf.java,v 1.20 2001/09/14 09:14:57 grosbois Exp $ * * Class: ForwCompTransf * * Description: Component transformations applied to tiles * * * * COPYRIGHT: * * This software module was originally developed by Raphaël Grosbois and * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research * Centre France S.A) in the course of development of the JPEG2000 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This * software module is an implementation of a part of the JPEG 2000 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio * Systems AB and Canon Research Centre France S.A (collectively JJ2000 * Partners) agree not to assert against ISO/IEC and users of the JPEG * 2000 Standard (Users) any of their rights under the copyright, not * including other intellectual property rights, for this software module * with respect to the usage by ISO/IEC and Users of this software module * or modifications thereof for use in hardware or software products * claiming conformance to the JPEG 2000 Standard. Those intending to use * this software module in hardware or software products are advised that * their use may infringe existing patents. The original developers of * this software module, JJ2000 Partners and ISO/IEC assume no liability * for use of this software module or modifications thereof. No license * or right to this software module is granted for non JPEG 2000 Standard * conforming products. JJ2000 Partners have full right to use this * software module for his/her own purpose, assign or donate this * software module to any third party and to inhibit third parties from * using this software module for non JPEG 2000 Standard conforming * products. This copyright notice must be included in all copies or * derivative works of this software module. * * Copyright (c) 1999/2000 JJ2000 Partners. * */ using System; using CSJ2K.j2k.wavelet.analysis; using CSJ2K.j2k.wavelet; using CSJ2K.j2k.encoder; using CSJ2K.j2k.image; using CSJ2K.j2k.util; using CSJ2K.j2k; namespace CSJ2K.j2k.image.forwcomptransf { /// This class apply component transformations to the tiles depending on user /// specifications. These transformations can be used to improve compression /// efficiency but are not related to colour transforms used to map colour /// values for display purposes. JPEG 2000 part I defines 2 component /// transformations: RCT (Reversible Component Transformation) and ICT /// (Irreversible Component Transformation). /// /// /// /// /// public class ForwCompTransf:ImgDataAdapter, BlkImgDataSrc { /// Returns the parameters that are used in this class and implementing /// classes. It returns a 2D String array. Each of the 1D arrays is for a /// different option, and they have 4 elements. The first element is the /// option name, the second one is the synopsis, the third one is a long /// description of what the parameter is and the fourth is its default /// value. The synopsis or description may be 'null', in which case it is /// assumed that there is no synopsis or description of the option, /// respectively. Null may be returned if no options are supported. /// /// /// the options name, their synopsis and their explanation, or null /// if no options are supported. /// /// public static System.String[][] ParameterInfo { get { return pinfo; } } /// Returns true if this transform is reversible in current /// tile. Reversible component transformations are those which operation /// can be completely reversed without any loss of information (not even /// due to rounding). /// /// /// Reversibility of component transformation in current tile /// /// virtual public bool Reversible { get { switch (transfType) { case NONE: case FORW_RCT: return true; case FORW_ICT: return false; default: throw new System.ArgumentException("Non JPEG 2000 part I" + " component transformation"); } } } /// Identifier for no component transformation. Value is 0. public const int NONE = 0; /// Identifier for the Forward Reversible Component Transformation /// (FORW_RCT). Value is 1. /// public const int FORW_RCT = 1; /// Identifier for the Forward Irreversible Component Transformation /// (FORW_ICT). Value is 2 /// public const int FORW_ICT = 2; /// The source of image data private BlkImgDataSrc src; /// The component transformations specifications private CompTransfSpec cts; /// The wavelet filter specifications private AnWTFilterSpec wfs; /// The type of the current component transformation. JPEG 2000 part 1 /// supports only NONE, FORW_RCT and FORW_ICT types /// private int transfType = NONE; /// The bit-depths of transformed components private int[] tdepth; /// Output block used instead of the one provided as an argument if the /// later is DataBlkFloat. /// private DataBlk outBlk; /// Block used to request component with index 0 private DataBlkInt block0; /// Block used to request component with index 1 private DataBlkInt block1; /// Block used to request component with index 2 private DataBlkInt block2; /// Constructs a new ForwCompTransf object that operates on the specified /// source of image data. /// /// /// The source from where to get the data to be transformed /// /// /// The encoder specifications /// /// /// /// /// public ForwCompTransf(BlkImgDataSrc imgSrc, EncoderSpecs encSpec):base(imgSrc) { this.cts = encSpec.cts; this.wfs = encSpec.wfs; src = imgSrc; } /// The prefix for component transformation type: 'M' public const char OPT_PREFIX = 'M'; /// The list of parameters that is accepted by the forward component /// transformation module. Options start with an 'M'. /// //UPGRADE_NOTE: Final was removed from the declaration of 'pinfo'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" private static readonly System.String[][] pinfo = new System.String[][]{new System.String[]{"Mct", "[] [on|off] ...", "Specifies in which tiles to use a multiple component transform. " + "Note that this multiple component transform can only be applied " + "in tiles that contain at least three components and whose " + "components are processed with the same wavelet filters and " + "quantization type. " + "If the wavelet transform is reversible (w5x3 filter), the " + "Reversible Component Transformation (RCT) is applied. If not " + "(w9x7 filter), the Irreversible Component Transformation (ICT)" + " is used.", null}}; /// Returns the position of the fixed point in the specified /// component. This is the position of the least significant integral /// (i.e. non-fractional) bit, which is equivalent to the number of /// fractional bits. For instance, for fixed-point values with 2 fractional /// bits, 2 is returned. For floating-point data this value does not apply /// and 0 should be returned. Position 0 is the position of the least /// significant bit in the data. /// ///

This default implementation assumes that the number of fractional /// bits is not modified by the component mixer.

/// ///
/// The index of the component. /// /// /// The value of the fixed point position of the source since the /// color transform does not affect it. /// /// public virtual int getFixedPoint(int c) { return src.getFixedPoint(c); } /// Calculates the bitdepths of the transformed components, given the /// bitdepth of the un-transformed components and the component /// transformation type. /// /// /// The bitdepth of each non-transformed components. /// /// /// The type ID of the component transformation. /// /// /// If not null the results are stored in this array, /// otherwise a new array is allocated and returned. /// /// /// The bitdepth of each transformed component. /// /// public static int[] calcMixedBitDepths(int[] ntdepth, int ttype, int[] tdepth) { if (ntdepth.Length < 3 && ttype != NONE) { throw new System.ArgumentException(); } if (tdepth == null) { tdepth = new int[ntdepth.Length]; } switch (ttype) { case NONE: Array.Copy(ntdepth, 0, tdepth, 0, ntdepth.Length); break; case FORW_RCT: if (ntdepth.Length > 3) { Array.Copy(ntdepth, 3, tdepth, 3, ntdepth.Length - 3); } // The formulas are: // tdepth[0] = ceil(log2(2^(ntdepth[0])+2^ntdepth[1]+ // 2^(ntdepth[2])))-2+1 // tdepth[1] = ceil(log2(2^(ntdepth[1])+2^(ntdepth[2])-1))+1 // tdepth[2] = ceil(log2(2^(ntdepth[0])+2^(ntdepth[1])-1))+1 // The MathUtil.log2(x) function calculates floor(log2(x)), so we // use 'MathUtil.log2(2*x-1)+1', which calculates ceil(log2(x)) // for any x>=1, x integer. tdepth[0] = MathUtil.log2((1 << ntdepth[0]) + (2 << ntdepth[1]) + (1 << ntdepth[2]) - 1) - 2 + 1; tdepth[1] = MathUtil.log2((1 << ntdepth[2]) + (1 << ntdepth[1]) - 1) + 1; tdepth[2] = MathUtil.log2((1 << ntdepth[0]) + (1 << ntdepth[1]) - 1) + 1; break; case FORW_ICT: if (ntdepth.Length > 3) { Array.Copy(ntdepth, 3, tdepth, 3, ntdepth.Length - 3); } // The MathUtil.log2(x) function calculates floor(log2(x)), so we // use 'MathUtil.log2(2*x-1)+1', which calculates ceil(log2(x)) // for any x>=1, x integer. //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'" tdepth[0] = MathUtil.log2((int) System.Math.Floor((1 << ntdepth[0]) * 0.299072 + (1 << ntdepth[1]) * 0.586914 + (1 << ntdepth[2]) * 0.114014) - 1) + 1; //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'" tdepth[1] = MathUtil.log2((int) System.Math.Floor((1 << ntdepth[0]) * 0.168701 + (1 << ntdepth[1]) * 0.331299 + (1 << ntdepth[2]) * 0.5) - 1) + 1; //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'" tdepth[2] = MathUtil.log2((int) System.Math.Floor((1 << ntdepth[0]) * 0.5 + (1 << ntdepth[1]) * 0.418701 + (1 << ntdepth[2]) * 0.081299) - 1) + 1; break; } return tdepth; } /// Initialize some variables used with RCT. It must be called, at least, /// at the beginning of each new tile. /// /// private void initForwRCT() { int i; int tIdx = TileIdx; if (src.NumComps < 3) { throw new System.ArgumentException(); } // Check that the 3 components have the same dimensions if (src.getTileCompWidth(tIdx, 0) != src.getTileCompWidth(tIdx, 1) || src.getTileCompWidth(tIdx, 0) != src.getTileCompWidth(tIdx, 2) || src.getTileCompHeight(tIdx, 0) != src.getTileCompHeight(tIdx, 1) || src.getTileCompHeight(tIdx, 0) != src.getTileCompHeight(tIdx, 2)) { throw new System.ArgumentException("Can not use RCT " + "on components with different " + "dimensions"); } // Initialize bitdepths int[] utd; // Premix bitdepths utd = new int[src.NumComps]; for (i = utd.Length - 1; i >= 0; i--) { utd[i] = src.getNomRangeBits(i); } tdepth = calcMixedBitDepths(utd, FORW_RCT, null); } /// Initialize some variables used with ICT. It must be called, at least, /// at the beginning of a new tile. /// /// private void initForwICT() { int i; int tIdx = TileIdx; if (src.NumComps < 3) { throw new System.ArgumentException(); } // Check that the 3 components have the same dimensions if (src.getTileCompWidth(tIdx, 0) != src.getTileCompWidth(tIdx, 1) || src.getTileCompWidth(tIdx, 0) != src.getTileCompWidth(tIdx, 2) || src.getTileCompHeight(tIdx, 0) != src.getTileCompHeight(tIdx, 1) || src.getTileCompHeight(tIdx, 0) != src.getTileCompHeight(tIdx, 2)) { throw new System.ArgumentException("Can not use ICT " + "on components with different " + "dimensions"); } // Initialize bitdepths int[] utd; // Premix bitdepths utd = new int[src.NumComps]; for (i = utd.Length - 1; i >= 0; i--) { utd[i] = src.getNomRangeBits(i); } tdepth = calcMixedBitDepths(utd, FORW_ICT, null); } /// Returns a string with a descriptive text of which forward component /// transformation is used. This can be either "Forward RCT" or "Forward /// ICT" or "No component transformation" depending on the current tile. /// /// /// A descriptive string /// /// public override System.String ToString() { switch (transfType) { case FORW_RCT: return "Forward RCT"; case FORW_ICT: return "Forward ICT"; case NONE: return "No component transformation"; default: throw new System.ArgumentException("Non JPEG 2000 part I" + " component transformation"); } } /// Returns the number of bits, referred to as the "range bits", /// corresponding to the nominal range of the data in the specified /// component and in the current tile. If this number is b then for /// unsigned data the nominal range is between 0 and 2^b-1, and for signed /// data it is between -2^(b-1) and 2^(b-1)-1. Note that this value can be /// affected by the multiple component transform. /// /// /// The index of the component. /// /// /// The bitdepth of component 'c' after mixing. /// /// public override int getNomRangeBits(int c) { switch (transfType) { case FORW_RCT: case FORW_ICT: return tdepth[c]; case NONE: return src.getNomRangeBits(c); default: throw new System.ArgumentException("Non JPEG 2000 part I" + " component transformation"); } } /// Apply forward component transformation associated with the current /// tile. If no component transformation has been requested by the user, /// data are not modified. /// ///

This method calls the getInternCompData() method, but respects the /// definitions of the getCompData() method defined in the BlkImgDataSrc /// interface.

/// ///
/// Determines the rectangular area to return, and the data is /// returned in this object. /// /// /// Index of the output component. /// /// /// The requested DataBlk /// /// /// /// /// public virtual DataBlk getCompData(DataBlk blk, int c) { // If requesting a component whose index is greater than 3 or there is // no transform return a copy of data (getInternCompData returns the // actual data in those cases) if (c >= 3 || transfType == NONE) { return src.getCompData(blk, c); } else { // We can use getInternCompData (since data is a copy anyways) return getInternCompData(blk, c); } } /// Apply the component transformation associated with the current tile. If /// no component transformation has been requested by the user, data are /// not modified. Else, appropriate method is called (forwRCT or forwICT). /// /// /// /// /// /// /// /// /// Determines the rectangular area to return. /// /// /// Index of the output component. /// /// /// The requested DataBlk /// /// public virtual DataBlk getInternCompData(DataBlk blk, int c) { switch (transfType) { case NONE: return src.getInternCompData(blk, c); case FORW_RCT: return forwRCT(blk, c); case FORW_ICT: return forwICT(blk, c); default: throw new System.ArgumentException("Non JPEG 2000 part 1 " + "component" + " transformation for tile: " + tIdx); } } /// Apply forward component transformation to obtain requested component /// from specified block of data. Whatever the type of requested DataBlk, /// it always returns a DataBlkInt. /// /// /// Determine the rectangular area to return /// /// /// The index of the requested component /// /// /// Data of requested component /// /// private DataBlk forwRCT(DataBlk blk, int c) { int k, k0, k1, k2, mink, i; int w = blk.w; //width of output block int h = blk.h; //height of ouput block int[] outdata; //array of output data //If asking for Yr, Ur or Vr do transform if (c >= 0 && c <= 2) { // Check that request data type is int if (blk.DataType != DataBlk.TYPE_INT) { if (outBlk == null || outBlk.DataType != DataBlk.TYPE_INT) { outBlk = new DataBlkInt(); } outBlk.w = w; outBlk.h = h; outBlk.ulx = blk.ulx; outBlk.uly = blk.uly; blk = outBlk; } //Reference to output block data array outdata = (int[]) blk.Data; //Create data array of blk if necessary if (outdata == null || outdata.Length < h * w) { outdata = new int[h * w]; blk.Data = outdata; } // Block buffers for input RGB data int[] data0, data1, bdata; // input data arrays if (block0 == null) block0 = new DataBlkInt(); if (block1 == null) block1 = new DataBlkInt(); if (block2 == null) block2 = new DataBlkInt(); block0.w = block1.w = block2.w = blk.w; block0.h = block1.h = block2.h = blk.h; block0.ulx = block1.ulx = block2.ulx = blk.ulx; block0.uly = block1.uly = block2.uly = blk.uly; //Fill in buffer blocks (to be read only) // Returned blocks may have different size and position block0 = (DataBlkInt) src.getInternCompData(block0, 0); data0 = (int[]) block0.Data; block1 = (DataBlkInt) src.getInternCompData(block1, 1); data1 = (int[]) block1.Data; block2 = (DataBlkInt) src.getInternCompData(block2, 2); bdata = (int[]) block2.Data; // Set the progressiveness of the output data blk.progressive = block0.progressive || block1.progressive || block2.progressive; blk.offset = 0; blk.scanw = w; //Perform conversion // Initialize general indexes k = w * h - 1; k0 = block0.offset + (h - 1) * block0.scanw + w - 1; k1 = block1.offset + (h - 1) * block1.scanw + w - 1; k2 = block2.offset + (h - 1) * block2.scanw + w - 1; switch (c) { case 0: //RGB to Yr conversion for (i = h - 1; i >= 0; i--) { for (mink = k - w; k > mink; k--, k0--, k1--, k2--) { // Use int arithmetic with 12 fractional bits // and rounding outdata[k] = (data0[k] + 2 * data1[k] + bdata[k]) >> 2; // Same as / 4 } // Jump to beggining of previous line in input k0 -= (block0.scanw - w); k1 -= (block1.scanw - w); k2 -= (block2.scanw - w); } break; case 1: //RGB to Ur conversion for (i = h - 1; i >= 0; i--) { for (mink = k - w; k > mink; k--, k1--, k2--) { // Use int arithmetic with 12 fractional bits // and rounding outdata[k] = bdata[k2] - data1[k1]; } // Jump to beggining of previous line in input k1 -= (block1.scanw - w); k2 -= (block2.scanw - w); } break; case 2: //RGB to Vr conversion for (i = h - 1; i >= 0; i--) { for (mink = k - w; k > mink; k--, k0--, k1--) { // Use int arithmetic with 12 fractional bits // and rounding outdata[k] = data0[k0] - data1[k1]; } // Jump to beggining of previous line in input k0 -= (block0.scanw - w); k1 -= (block1.scanw - w); } break; } } else if (c >= 3) { // Requesting a component which is not Y, Ur or Vr => // just pass the data return src.getInternCompData(blk, c); } else { // Requesting a non valid component index throw new System.ArgumentException(); } return blk; } /// Apply forward irreversible component transformation to obtain requested /// component from specified block of data. Whatever the type of requested /// DataBlk, it always returns a DataBlkFloat. /// /// /// Determine the rectangular area to return /// /// /// The index of the requested component /// /// /// Data of requested component /// /// private DataBlk forwICT(DataBlk blk, int c) { int k, k0, k1, k2, mink, i; int w = blk.w; //width of output block int h = blk.h; //height of ouput block float[] outdata; //array of output data if (blk.DataType != DataBlk.TYPE_FLOAT) { if (outBlk == null || outBlk.DataType != DataBlk.TYPE_FLOAT) { outBlk = new DataBlkFloat(); } outBlk.w = w; outBlk.h = h; outBlk.ulx = blk.ulx; outBlk.uly = blk.uly; blk = outBlk; } //Reference to output block data array outdata = (float[]) blk.Data; //Create data array of blk if necessary if (outdata == null || outdata.Length < w * h) { outdata = new float[h * w]; blk.Data = outdata; } //If asking for Y, Cb or Cr do transform if (c >= 0 && c <= 2) { int[] data0, data1, data2; // input data arrays if (block0 == null) { block0 = new DataBlkInt(); } if (block1 == null) { block1 = new DataBlkInt(); } if (block2 == null) { block2 = new DataBlkInt(); } block0.w = block1.w = block2.w = blk.w; block0.h = block1.h = block2.h = blk.h; block0.ulx = block1.ulx = block2.ulx = blk.ulx; block0.uly = block1.uly = block2.uly = blk.uly; // Returned blocks may have different size and position block0 = (DataBlkInt) src.getInternCompData(block0, 0); data0 = (int[]) block0.Data; block1 = (DataBlkInt) src.getInternCompData(block1, 1); data1 = (int[]) block1.Data; block2 = (DataBlkInt) src.getInternCompData(block2, 2); data2 = (int[]) block2.Data; // Set the progressiveness of the output data blk.progressive = block0.progressive || block1.progressive || block2.progressive; blk.offset = 0; blk.scanw = w; //Perform conversion // Initialize general indexes k = w * h - 1; k0 = block0.offset + (h - 1) * block0.scanw + w - 1; k1 = block1.offset + (h - 1) * block1.scanw + w - 1; k2 = block2.offset + (h - 1) * block2.scanw + w - 1; switch (c) { case 0: //RGB to Y conversion for (i = h - 1; i >= 0; i--) { for (mink = k - w; k > mink; k--, k0--, k1--, k2--) { outdata[k] = 0.299f * data0[k0] + 0.587f * data1[k1] + 0.114f * data2[k2]; } // Jump to beggining of previous line in input k0 -= (block0.scanw - w); k1 -= (block1.scanw - w); k2 -= (block2.scanw - w); } break; case 1: //RGB to Cb conversion for (i = h - 1; i >= 0; i--) { for (mink = k - w; k > mink; k--, k0--, k1--, k2--) { outdata[k] = (- 0.16875f) * data0[k0] - 0.33126f * data1[k1] + 0.5f * data2[k2]; } // Jump to beggining of previous line in input k0 -= (block0.scanw - w); k1 -= (block1.scanw - w); k2 -= (block2.scanw - w); } break; case 2: //RGB to Cr conversion for (i = h - 1; i >= 0; i--) { for (mink = k - w; k > mink; k--, k0--, k1--, k2--) { outdata[k] = 0.5f * data0[k0] - 0.41869f * data1[k1] - 0.08131f * data2[k2]; } // Jump to beggining of previous line in input k0 -= (block0.scanw - w); k1 -= (block1.scanw - w); k2 -= (block2.scanw - w); } break; } } else if (c >= 3) { // Requesting a component which is not Y, Cb or Cr => // just pass the data // Variables DataBlkInt indb = new DataBlkInt(blk.ulx, blk.uly, w, h); int[] indata; // input data array // Get the input data // (returned block may be larger than requested one) src.getInternCompData(indb, c); indata = (int[]) indb.Data; // Copy the data converting from int to float k = w * h - 1; k0 = indb.offset + (h - 1) * indb.scanw + w - 1; for (i = h - 1; i >= 0; i--) { for (mink = k - w; k > mink; k--, k0--) { //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'" outdata[k] = (float) indata[k0]; } // Jump to beggining of next line in input k0 += indb.w - w; } // Set the progressivity blk.progressive = indb.progressive; blk.offset = 0; blk.scanw = w; return blk; } else { // Requesting a non valid component index throw new System.ArgumentException(); } return blk; } /// Changes the current tile, given the new indexes. An /// IllegalArgumentException is thrown if the indexes do not correspond to /// a valid tile. /// ///

This default implementation changes the tile in the source and /// re-initializes properly component transformation variables..

/// ///
/// The horizontal index of the tile. /// /// /// The vertical index of the new tile. /// /// public override void setTile(int x, int y) { src.setTile(x, y); tIdx = TileIdx; // index of the current tile // initializations System.String str = (System.String) cts.getTileDef(tIdx); if (str.Equals("none")) { transfType = NONE; } else if (str.Equals("rct")) { transfType = FORW_RCT; initForwRCT(); } else if (str.Equals("ict")) { transfType = FORW_ICT; initForwICT(); } else { throw new System.ArgumentException("Component transformation" + " not recognized"); } } /// Goes to the next tile, in standard scan-line order (by rows then by /// columns). An NoNextElementException is thrown if the current tile is /// the last one (i.e. there is no next tile). /// ///

This default implementation just advances to the next tile in the /// source and re-initializes properly component transformation /// variables.

/// ///
public override void nextTile() { src.nextTile(); tIdx = TileIdx; // index of the current tile // initializations System.String str = (System.String) cts.getTileDef(tIdx); if (str.Equals("none")) { transfType = NONE; } else if (str.Equals("rct")) { transfType = FORW_RCT; initForwRCT(); } else if (str.Equals("ict")) { transfType = FORW_ICT; initForwICT(); } else { throw new System.ArgumentException("Component transformation" + " not recognized"); } } } }