eLynx SDK
v3.3.0 C++ image processing API reference |
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?
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);
Resolution | Hue range | Saturation range |
---|---|---|
ubyte | [0, HueMax_ubyte] | [0, ResolutionTypeTraits<ubyte>::_Max] |
ushort | [0, HueMax_ushort] | [0, ResolutionTypeTraits<ushort>::_Max] |
int | [0, HueMax_int] | [0, ResolutionTypeTraits<int>::_Max] |
float | [0.0f, 1.0f] | [0.0f, 1.0f] |
double | [0.0, 1.0] | [0.0, 1.0] |
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));}
Now we need to implement elxColorize for all pixel types.
This means for:
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
How to cover all colored pixels? They are two problematics:
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
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
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
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); } };
//---------------------------------------------------------------------------- // 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
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.