eLynx SDK
v3.0.1 C++ image processing API reference |
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.