eLynx SDK v3.3.0
C++ image processing API reference

How to add pixel services?

Here is a trip into pixel format.


To illustrate the tutorial let's code the elxColorize method.
The heart of Colorize filter implementation is at pixel low level. Pixel generalization is not an easy task.
The work is done in elxColorize method implementation, into eLynx/src/Image/ColorSpace/HLS.hpp file.
Colorize is just setting hue and saturation for a pixel in HLS color space.

Follow these steps:


Next see article How to add image services?


Define pixel services prototype

The Colorize filter sets the hue and the saturation of an image. We need a service for each pixel types:

template<class Pixel> 
void elxColorize(Pixel& ioPixel, 
    typename Pixel::type iHue, 
    typename Pixel::type iSaturation);

So we need two more services to compute user to pixel parameter values:

// hue max is a multiple of 6 for integer types
template<typename T> inline T elxGetHueMax();
template<> inline ubyte  elxGetHueMax<ubyte >() { return 252; }
template<> inline ushort elxGetHueMax<ushort>() { return 65532; }
template<> inline int    elxGetHueMax<int   >() { return 600000; }

//----------------------------------------------------------------------------
//  elxGetHue
//----------------------------------------------------------------------------
template<typename T> inline
T elxGetHue(double iHue, FloatType)   { return T(iHue); }

template<typename T> inline
T elxGetHue(double iHue, IntegerType) { return T(elxGetHueMax<T>() * iHue + 0.5); }

template<typename T> inline
T elxGetHue(double iHue) { return elxGetHue<T>(iHue, RESOLUTION_TYPE(T)); }

//----------------------------------------------------------------------------
//  elxGetSaturation
//----------------------------------------------------------------------------
template<typename T> inline
T elxGetSaturation(double iSaturation, FloatType) { return T(iSaturation); }

template<typename T> inline
T elxGetSaturation(double iSaturation, IntegerType)
{ return T(iSaturation * ResolutionTypeTraits<T>::_Max + 0.5); }

template<typename T> inline
T elxGetSaturation(double iSaturation) 
{ return elxGetSaturation<T>(iSaturation, RESOLUTION_TYPE(T));}

Go to top


Analyse how to implement all pixel format

Now we need to implement elxColorize for all pixel types.
This means for:

Go to top


Implementation for non-colored pixels

For PixelL<T>, PixelLA<T> and PixelComplex<T> implementation does nothing.

//----------------------------------------------------------------------------
//  elxColorize # NonColorType
//----------------------------------------------------------------------------
template<class Pixel> 
inline
void elxColorize(Pixel& ioPixel, 
    typename Pixel::type iHue, 
    typename Pixel::type iSaturation,
    NonColorType)
{
  //  Do nothing

} // elxColorize # NonColorType

Go to top


Implementation for colored pixels

How to cover all colored pixels? They are two problematics:

Go to top


Specialization for HLS pixels

HLS is the space with the trivial processing. The specialization for PixelHLS<T>:

//----------------------------------------------------------------------------
//  elxColorizeHLS
//----------------------------------------------------------------------------
template<typename T>
inline
void elxColorizeHLS(PixelHLS<T>& ioPixel, T iHue, T iSaturation)
{
  ioPixel._hue = iHue;
  ioPixel._saturation = iSaturation;

} // elxColorizeHLS

Go to top


Specialization for RGB pixels

RGB is the best color space for conversion. So we implement a specialization for RGB and use it for all other color space. We must take care that no conversion to PixelHLS<integer type> are supported. Only PixelHLS<float> and PixelHLS<double> are supported. So we need to implement specialization for RGB floating types and another for integer types:

//----------------------------------------------------------------------------
//  elxColorizeRGB # FloatType
//----------------------------------------------------------------------------
//  iHue in range [0, 1]
//  iSaturation in range [0, 1]
//----------------------------------------------------------------------------
template<typename T>
inline
void elxColorizeRGB(PixelRGB<T>& ioPixel, T iHue, T iSaturation, FloatType)
{
  const T r = ioPixel._red;
  const T g = ioPixel._green;
  const T b = ioPixel._blue;

  // compute lightness
  const T Max = Math::elxMax(r,g,b);
  const T Min = Math::elxMin(r,g,b);
  const T l = (Max + Min)/2;
  if (T(0) == iSaturation)
  {
    // achromatic case
    ioPixel._red = ioPixel._green = ioPixel._blue = l;
    return;
  }

  const T s = iSaturation;
  const T p2 = (l <= T(0.5)) ? l*(T(1) + s) : l*(T(1) - s) + s;
  const T p1 = T(2)*l - p2;
  const T h = 6*iHue;
  
  ioPixel._red   = elxHueToRGB(p1, p2, h+2, RESOLUTION_TYPE(T));
  ioPixel._green = elxHueToRGB(p1, p2, h,   RESOLUTION_TYPE(T));
  ioPixel._blue  = elxHueToRGB(p1, p2, h-2, RESOLUTION_TYPE(T));

} // elxColorizeRGB # FloatType 

//----------------------------------------------------------------------------
//  elxColorizeRGB # IntegerType
//----------------------------------------------------------------------------
//  iHue in range [0, elxGetHueMax<T>()]
//  iSaturation in range [0, ResolutionTypeTraits<T>::_Max]
//----------------------------------------------------------------------------
template<typename T>
inline
void elxColorizeRGB(PixelRGB<T>& ioPixel, T iHue, T iSaturation, IntegerType)
{
  const T M = ResolutionTypeTraits<T>::_Max;
  const T HueMax = elxGetHueMax<T>();

  T r = ioPixel._red;
  T g = ioPixel._green;
  T b = ioPixel._blue;

  // compute lightness
  const T Max = Math::elxMax(r,g,b);
  const T Min = Math::elxMin(r,g,b);
  const T l = (Max + Min)/2;
  if (0 == iSaturation)
  {
    // achromatic case
    ioPixel._red = ioPixel._green = ioPixel._blue = l;
    return;
  }
  
  const T s = iSaturation;
  const T p2 = (l <= M/2) ? (l*(M + s) + M/2)/M : l + s - (l*s + M/2)/M;
  const T p1 = 2*l - p2;

  // get RGB, change units from hue max to M
  r = elxHueToRGB(p1, p2, T(iHue + HueMax/3), RESOLUTION_TYPE(T));
  g = elxHueToRGB(p1, p2, iHue,               RESOLUTION_TYPE(T));
  b = elxHueToRGB(p1, p2, T(iHue - HueMax/3), RESOLUTION_TYPE(T));

  ioPixel._red   = (M*r + M/2)/M;
  ioPixel._green = (M*g + M/2)/M;
  ioPixel._blue  = (M*b + M/2)/M;

} // elxColorizeRGB # IntegerType

Go to top


Generalization for all other pixels

Now we could write the generalized implementation for color pixels:

//----------------------------------------------------------------------------
//  elxColorize # ColorType
//----------------------------------------------------------------------------
template<class Pixel> 
inline
void elxColorize(Pixel& ioPixel, 
    typename Pixel::type iHue, 
    typename Pixel::type iSaturation,
    ColorType)
{
  typedef typename Pixel::type T;

  // convert to RGB<T>, Colorize, back to original color space
  PixelRGB<T> rgb(ioPixel);
  elxColorizeRGB(rgb, iHue, iSaturation, RESOLUTION_TYPE(T));
  ioPixel = Pixel(rgb);

} // elxColorize # ColorType

Go to top


Helper to access all methods as one

Finally, we need to code a helper and his specializations to access to all methods as one:

template<class Pixel> 
struct ColorizeHelper
{
  static void Do(Pixel& ioPixel, typename Pixel::type iHue, typename Pixel::type iSaturation)
  { elxColorize(ioPixel, iHue, iSaturation, PIXEL_COLOR_TYPE(Pixel)); }
};

template<typename T> 
struct ColorizeHelper< PixelRGB<T> >
{
  static void Do(PixelRGB<T>& ioPixel, T iHue, T iSaturation)
  { elxColorizeRGB(ioPixel, iHue, iSaturation, RESOLUTION_TYPE(T)); }
};

template<typename T> 
struct ColorizeHelper< PixelHLS<T> >
{
  static void Do(PixelHLS<T>& ioPixel, T iHue, T iSaturation)
  { elxColorizeHLS(ioPixel, iHue, iSaturation); }
};

Go to top


The final generic service

//----------------------------------------------------------------------------
//  elxColorize # generic
//----------------------------------------------------------------------------
template<class Pixel> 
inline
void elxColorize(Pixel& ioPixel, 
    typename Pixel::type iHue, 
    typename Pixel::type iSaturation)
{
  ColorizeHelper<Pixel>::Do(ioPixel, iHue, iSaturation);

} // elxColorize # generic

Go to top


The service instanciation

The last thing to do is to explicitly instantiate elxColorize for all pixel types. Define a macro for MACRO_COLORIZE to define method signature, then invoke one of the elxINSTANTIATE_XXX macros.

//--- elxColorize ------------------------------------------------------------
#define MACRO_COLORIZE(Pixel, T) \
  template void elxColorize< Pixel<T> >(Pixel<T>&, T, T);

elxINSTANTIATE_METHOD_FOR_ALL_PIXEL_TYPES2( MACRO_COLORIZE );

For more details about template techniques, feel free to contact the team guru: Mike.

Go to top


Generated on Thu Dec 9 2010 by doxygen 1.7.2