gml_LRGColor.h

00001 // gml_LRGColor.h --
00002 // 
00003 //  Defines the gml_TColor_LRG class template.
00004 // 
00005 // Copyright (c) 2004 CLIPS-IMAG
00006 // 
00007 // See the file "gml_LicenseTerms.txt" for information on usage and redistribution
00008 // of this file, and for a DISCLAIMER OF ALL WARRANTIES.
00009 // 
00010 //  Created on March 14, 2003 (JL).
00011 //  Updated to GML on January 12, 2004 (JL).
00012 
00013 #ifndef __GML_LRGCOLOR__
00014 #define __GML_LRGCOLOR__
00015 
00016 #include <assert.h>
00017 #include <stdio.h>
00018 #include <float.h>
00019 
00020 #include "gml/base/gml_Types.h"
00021 #include "gml/base/gml_BytePack.h"
00022 #include "gml/base/gml_Attributes.h"
00023 #include "gml/math/gml_Tables.h"
00024 #include "gml/math/gml_Math.h"
00025 
00026 ///
00027 /// gml_TColor_LRG --
00028 ///   A union type used to represent LRG-color pixels.
00029 ///   LRG stands for Luminance, Normalized Red, Normalized Green colorspace.
00030 ///
00031 ///   The channels' storage type is specified as template parameter <Channel>.
00032 ///   The second template parameter <KSize> specifies an integer type large 
00033 ///   enough to hold all computations.
00034 ///
00035 ///   @bug
00036 ///   The following should be true, but for now we assume that: 
00037 ///   <pre>sChannelMax == 1 << sChannelBits</pre> in Convert().
00038 ///   This of course leads to some rounding errors, max 4/255.
00039 ///
00040 ///   Note that the full range of data that a channel can represent isn't used:
00041 ///   for instance L values range from 0 to <tt>2 ** (sChannelBits() - 1)</tt>.
00042 ///   This is done to accelerate some computation, e.g. replacing divisions by 
00043 ///   sChannelMax() by bit-shifting operations.
00044 ///
00045 ///   @note 
00046 ///   Depending on the machine, this code is 10 to 20 times faster than
00047 ///   OpenCV's version, which uses euclidian divisions extensively. We use a
00048 ///   small (3k) lookup table and lots of bit-shifting.
00049 ///
00050 template <typename Channel, typename KSize>
00051 class gml_TColor_LRG
00052 {
00053 public:
00054 
00055   ///
00056   /// Constructor --
00057   ///   Convert a 24-bit RGB triplet into LRG and fill in the object.
00058   ///
00059   gml_TColor_LRG (const UInt8 r, const UInt8 g, const UInt8 b) {
00060     #ifndef __GML_AVOID_EUCLIDIAN_DIVISION
00061       UInt32 const sum = (UInt32) r + g + b;
00062       UInt32 const denom = Max (1UL, (UInt32) r + g + b);
00063       L = (sChannelMax() * sum + 765/2) / 765;
00064       R = (sChannelMax() * r   + denom/2) / denom;
00065       G = (sChannelMax() * g   + denom/2) / denom;
00066     #else
00067       UInt32 const sum = (UInt32) r + g + b;
00068       // avoid doing a division by 3*255 by noting that 3*255  ~=  2^24 / 21931  ~=  2^17 / 171
00069       // the (1<<23) allows for rounding division (rather than flooring divison)
00070       L = ShiftRight (KSize(21931) * sChannelMax() * sum + (1<<23), 24);
00071       // avoid divisions by <sum> by using a tabulated reciprocal
00072       UInt32 const mantissa = ReciprocalMantissa_15_10bit (sum);
00073       KSize const tR = ShiftRight (sChannelMax() * r * mantissa + (1<<14), 15);
00074       KSize const tG = ShiftRight (sChannelMax() * g * mantissa + (1<<14), 15);
00075       R = Min (sChannelMax(), tR);
00076       G = Min (sChannelMax(), tG);
00077       
00078 //      assert (R+G < sChannelMax()); // this should pass, but fails miserably
00079     #endif /* __GML_AVOID_EUCLIDIAN_DIVISION */
00080   }
00081   
00082   ///
00083   /// Constructor --
00084   ///   Fill the structure from data in memory.
00085   ///
00086   gml_TColor_LRG (const void * const ptr) {
00087     *this = *((gml_TColor_LRG<Channel,KSize>*)ptr);
00088   }
00089 
00090   ///
00091   /// Constructor --
00092   ///   Leave structure `as is'.
00093   ///
00094   gml_TColor_LRG () {}
00095 
00096 
00097   ///
00098   /// Print --
00099   ///   Dump a representation of the pixel to stdout: channels in hex and
00100   ///   in 8-bit per channel hex format.
00101   ///   @warning no newline appended !
00102   ///
00103   void Print () {
00104     printf ("% 8X % 8X % 8X (%02X %02X %02X)", 
00105       L, R, G,
00106       L >> (sChannelBits() - 8), 
00107       R >> (sChannelBits() - 8), 
00108       G >> (sChannelBits() - 8)
00109       );
00110   }
00111   
00112   
00113   ///
00114   /// Store --
00115   ///   Place the structure's data in memory
00116   ///
00117   void Store (void * const ptr) {
00118     *((gml_TColor_LRG<Channel,KSize>*)ptr) = *this;
00119   }
00120   
00121   
00122   ///
00123   /// Convert --
00124   ///   Convert the LRG data in this object to a 24 bit RGB triplet.
00125   ///
00126   void Convert (UInt8 &r, UInt8 &g, UInt8 &b) {
00127     /// @todo replace the ?: selector with Select()
00128     #ifndef __GML_AVOID_EUCLIDIAN_DIVISION
00129       KSize   const denom = sChannelMax() * sChannelMax();
00130       Channel const B = ((KSize) R+G >= sChannelMax()) ? 0 : (sChannelMax() - (R+G));
00131 
00132       UInt32  const tr = (KSize(765) * L * R + denom/2) / denom;
00133       UInt32  const tg = (KSize(765) * L * G + denom/2) / denom;
00134       UInt32  const tb = (KSize(765) * L * B + denom/2) / denom;
00135       r = Min (tr, 255UL);
00136       g = Min (tg, 255UL);
00137       b = Min (tb, 255UL);
00138     #else
00139       KSize   const delta = (1 << (2*sChannelBits()-9)); // for rounding division
00140       Channel const B = ((KSize) R+G >= sChannelMax()) ? 0 : (sChannelMax() - (R+G));
00141 
00142       UInt32  const tr = (KSize(3) * L * R + delta) >> (2*sChannelBits()-8);
00143       UInt32  const tg = (KSize(3) * L * G + delta) >> (2*sChannelBits()-8);
00144       UInt32  const tb = (KSize(3) * L * B + delta) >> (2*sChannelBits()-8);
00145       r = Min (tr, 255UL);
00146       g = Min (tg, 255UL);
00147       b = Min (tb, 255UL);
00148     #endif
00149   }
00150   
00151   
00152   ///
00153   /// Distance --
00154   ///   Compute the 16-bit distance to another LRG pixel.
00155   ///   Depending on __GML_LRG_USE_EUCLIDIAN_DISTANCE,
00156   ///   the euclidian distance in the (Cr,Cg) plane is used, or
00157   ///   the city block distance.
00158   ///
00159   UInt16 Distance (const gml_TColor_LRG<Channel,KSize> other) {
00160     return Min (65535UL,
00161     #ifdef __GML_LRGCOLOR_USE_EUCLIDIAN_DISTANCE
00162       64UL * Norm (
00163           ShiftRight (R,       sChannelBits() - 12), 
00164           ShiftRight (G,       sChannelBits() - 12), 
00165           ShiftRight (other.R, sChannelBits() - 12), 
00166           ShiftRight (other.G, sChannelBits() - 12)
00167         )
00168     #else
00169       (UInt32)
00170       ShiftLeft (
00171         KSize (Abs (KSize (other.R) - KSize (R)) + 
00172                Abs (KSize (other.G) - KSize (G))),
00173         18 - sChannelBits()
00174       )
00175     #endif
00176     );
00177   }
00178 
00179   inline static
00180   UInt8 sChannelBits() { return 8 * sizeof (Channel); }         ///< bits per channel
00181   inline static
00182   KSize sChannelMax()  { return (1ULL << sChannelBits()) - 1; } ///< max channel value
00183 
00184   struct {                                ///< structure for transparent channel access
00185     #if BYTE_ORDER == BIG_ENDIAN
00186       Channel L;                          ///< luminance channel
00187       Channel R;                          ///< red chrominance channel
00188       Channel G;                          ///< green chrominance channel
00189     #else
00190       Channel G;                          ///< green chrominance channel
00191       Channel R;                          ///< red chrominance channel
00192       Channel L;                          ///< luminance channel
00193     #endif
00194   } GML_PACKED;
00195   
00196 };
00197 
00198 
00199 //
00200 // gml_TColor_LRG_888, gml_TColor_LRG_SSS, gml_TColor_LRG_LLL --
00201 //   24-bit, 48-bit, and 96-bit structures for LRG-color pixels.
00202 //
00203 typedef gml_TColor_LRG<UInt8,  SInt32> gml_TColor_LRG_888;
00204 typedef gml_TColor_LRG<UInt16, SInt64> gml_TColor_LRG_SSS;
00205 typedef gml_TColor_LRG<UInt32, SInt64> gml_TColor_LRG_LLL;
00206 
00207 ///
00208 /// gml_TColor_LRG_FFF --
00209 ///   Floating-point variant of gml_TColor_LRG.
00210 ///
00211 class gml_TColor_LRG_FFF : public gml_TColor_LRG<Float32, Float32>
00212 {
00213 public:
00214 
00215   ///
00216   /// Constructor --
00217   ///   Convert a 24-bit RGB triplet into LRG and fill in the object.
00218   ///
00219   gml_TColor_LRG_FFF (const UInt8 r, const UInt8 g, const UInt8 b) {
00220     Float32 const sum = (Float32) r + g + b; 
00221     L = Divide (sum, 765.0F);
00222     R = Divide (r, sum + FLT_EPSILON);
00223     G = Divide (g, sum + FLT_EPSILON);
00224     assert (0.0F <= L && L <= 1.0F);
00225     assert (0.0F <= R && R <= 1.0F);
00226     assert (0.0F <= G && G <= 1.0F);
00227   }
00228   
00229   ///
00230   /// Constructor --
00231   ///   Fill the structure from data in memory.
00232   ///
00233   gml_TColor_LRG_FFF (const void * const ptr) {
00234     *this = *((gml_TColor_LRG_FFF*)ptr);
00235 
00236     assert (0.0F <= L && L <= 1.0F);
00237     assert (0.0F <= R && R <= 1.0F);
00238     assert (0.0F <= G && G <= 1.0F);
00239   }
00240 
00241   ///
00242   /// Print --
00243   ///   Dump a representation of the pixel to stdout: channels in hex and
00244   ///   in 8-bit per channel hex format.
00245   ///   @warning no newline appended !
00246   ///
00247   void Print () {
00248     printf ("%f %f %f (%02X %02X %02X)", 
00249       L, R, G,
00250       (unsigned int) LFloor (255.0F * L), 
00251       (unsigned int) LFloor (255.0F * R), 
00252       (unsigned int) LFloor (255.0F * G)
00253       );
00254   }
00255   
00256   
00257   ///
00258   /// Convert --
00259   ///   Convert the LRG data in this object to a 24 bit RGB triplet.
00260   ///
00261   void Convert (UInt8 &r, UInt8 &g, UInt8 &b) {
00262     r = LRound (765.0F * L * R);
00263     g = LRound (765.0F * L * G);
00264     b = LRound (765.0F * L * (1.0F - R - G));
00265   }
00266   
00267   
00268   ///
00269   /// Distance --
00270   ///   Compute the 16-bit distance to another LRG pixel.
00271   ///   Depending on __GML_LRG_USE_EUCLIDIAN_DISTANCE,
00272   ///   the euclidian distance in the (Cr,Cg) plane is used, or
00273   ///   the city block distance.
00274   ///
00275   UInt16 Distance (const gml_TColor_LRG_FFF other) {
00276     return Min (65535UL,
00277     #ifdef __GML_LRGCOLOR_USE_EUCLIDIAN_DISTANCE
00278       LFloor (64.0F * 255.0F * Norm (R, G, other.R, other.G))
00279     #else
00280       LFloor (1024.0F * 255.0F * (Abs (other.R - R) + Abs (other.G - G)))
00281     #endif
00282     );
00283   }
00284 
00285   static 
00286   Float32 sChannelMax() { return 1.0F; };   ///< max channel value
00287 };
00288 
00289 #endif /* __GML_LRGCOLOR__ */
00290 
Generated on Tue Jun 12 14:03:27 2007 for gml by Doxygen 1.5.2.