// Copyright (C) 2006 Davis E. King (davisking@users.sourceforge.net) // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_PIXEl_ #define DLIB_PIXEl_ #include <iostream> #include "serialize.h" #include <cmath> #include "algs.h" #include "uintn.h" #include <limits> #include "enable_if.h" namespace dlib { // ---------------------------------------------------------------------------------------- /*! This file contains definitions of pixel objects and related classes and functionality. !*/ // ---------------------------------------------------------------------------------------- template <typename T> struct pixel_traits; /*! WHAT THIS OBJECT REPRESENTS As the name implies, this is a traits class for pixel types. It defines the properties of a pixel (duah). This traits class will define the following public static members: - bool grayscale - bool rgb - bool rgb_alpha - bool hsi - bool has_alpha - long num - unsigned long max() The above public constants are subject to the following constraints: - only one of grayscale, rgb, rgb_alpha, or hsi is true - if (rgb == true) then - The type T will be a struct with 3 public members of type unsigned char named "red" "green" and "blue". - This type of pixel represents the RGB color space. - num == 3 - has_alpha == false - max() == 255 - if (rgb_alpha == true) then - The type T will be a struct with 4 public members of type unsigned char named "red" "green" "blue" and "alpha". - This type of pixel represents the RGB color space with an alpha channel where an alpha of 0 represents a pixel that is totally transparent and 255 represents a pixel with maximum opacity. - num == 4 - has_alpha == true - max() == 255 - else if (hsi == true) then - The type T will be a struct with 3 public members of type unsigned char named "h" "s" and "i". - This type of pixel represents the HSI color space. - num == 3 - has_alpha == false - max() == 255 - else - grayscale == true - The type T will be an unsigned integral type. - This type of pixel represents a grayscale color space - num == 1 - has_alpha == false - max() == std::numeric_limits<T>::max() !*/ // ---------------------------------------------------------------------------------------- struct rgb_pixel { /*! WHAT THIS OBJECT REPRESENTS This is a simple struct that represents an RGB colored graphical pixel. !*/ rgb_pixel ( ) {} rgb_pixel ( unsigned char red_, unsigned char green_, unsigned char blue_ ) : red(red_), green(green_), blue(blue_) {} unsigned char red; unsigned char green; unsigned char blue; }; // ---------------------------------------------------------------------------------------- struct rgb_alpha_pixel { /*! WHAT THIS OBJECT REPRESENTS This is a simple struct that represents an RGB colored graphical pixel with an alpha channel. !*/ rgb_alpha_pixel ( ) {} rgb_alpha_pixel ( unsigned char red_, unsigned char green_, unsigned char blue_, unsigned char alpha_ ) : red(red_), green(green_), blue(blue_), alpha(alpha_) {} unsigned char red; unsigned char green; unsigned char blue; unsigned char alpha; }; // ---------------------------------------------------------------------------------------- struct hsi_pixel { /*! WHAT THIS OBJECT REPRESENTS This is a simple struct that represents an HSI colored graphical pixel. !*/ hsi_pixel ( ) {} hsi_pixel ( unsigned char h_, unsigned char s_, unsigned char i_ ) : h(h_), s(s_), i(i_) {} unsigned char h; unsigned char s; unsigned char i; }; // ---------------------------------------------------------------------------------------- template < typename P1, typename P2 > inline void assign_pixel ( P1& dest, const P2& src ); /*! requires - pixel_traits<P1> must be defined - pixel_traits<P2> must be defined ensures - if (P1 and P2 are the same type of pixel) then - simply coppies the value of src into dest. In other words, dest will be identical to src after this function returns. - else if (P1 and P2 are not the same type of pixel) then - assigns pixel src to pixel dest and does any necessary color space conversions. - When converting from a grayscale color space with more than 255 values the pixel intensity is saturated at pixel_traits<P1>::max(). - if (the dest pixel has an alpha channel and the src pixel doesn't) then - #dest.alpha == 255 - else if (the src pixel has an alpha channel but the dest pixel doesn't) then - #dest == the original dest value blended with the src value according to the alpha channel in src. (i.e. #dest == src*(alpha/255) + dest*(1-alpha/255)) !*/ template < typename P > inline void assign_pixel ( P& dest, const int src ); /*! requires - pixel_traits<P> must be defined ensures - performs assign_pixel(dest, static_cast<unsigned long>(max(0,src))) !*/ template < typename P > inline void assign_pixel ( P& dest, const long src ); /*! requires - pixel_traits<P> must be defined ensures - performs assign_pixel(dest, static_cast<unsigned long>(max(0,src))) !*/ // ---------------------------------------------------------------------------------------- template < typename P > inline unsigned long get_pixel_intensity ( const P& src ); /*! requires - pixel_traits<P> must be defined ensures - if (pixel_traits<P>::grayscale == true) then - returns src - else - converts src to the HSI color space and returns the intensity !*/ // ---------------------------------------------------------------------------------------- template < typename P > inline void assign_pixel_intensity ( P& dest, const unsigned long new_intensity ); /*! requires - pixel_traits<P> must be defined ensures - let val == min(new_intensity, pixel_traits<P>::max()) - #get_pixel_intensity(dest) == val - if (the dest pixel has an alpha channel) then - #dest.alpha == dest.alpha !*/ // ---------------------------------------------------------------------------------------- void serialize ( const rgb_pixel& item, std::ostream& out ); /*! provides serialization support for the rgb_pixel struct !*/ // ---------------------------------------------------------------------------------------- void deserialize ( rgb_pixel& item, std::istream& in ); /*! provides deserialization support for the rgb_pixel struct !*/ // ---------------------------------------------------------------------------------------- void serialize ( const rgb_alpha_pixel& item, std::ostream& out ); /*! provides serialization support for the rgb_alpha_pixel struct !*/ // ---------------------------------------------------------------------------------------- void deserialize ( rgb_alpha_pixel& item, std::istream& in ); /*! provides deserialization support for the rgb_alpha_pixel struct !*/ // ---------------------------------------------------------------------------------------- void serialize ( const hsi_pixel& item, std::ostream& out ); /*! provides serialization support for the hsi_pixel struct !*/ // ---------------------------------------------------------------------------------------- void deserialize ( hsi_pixel& item, std::istream& in ); /*! provides deserialization support for the hsi_pixel struct !*/ // ---------------------------------------------------------------------------------------- template <> struct pixel_traits<rgb_pixel> { const static bool rgb = true; const static bool rgb_alpha = false; const static bool grayscale = false; const static bool hsi = false; const static long num = 3; static unsigned long max() { return 255;} const static bool has_alpha = false; }; // ---------------------------------------------------------------------------------------- template <> struct pixel_traits<rgb_alpha_pixel> { const static bool rgb = false; const static bool rgb_alpha = true; const static bool grayscale = false; const static bool hsi = false; const static long num = 4; static unsigned long max() { return 255; } const static bool has_alpha = true; }; // ---------------------------------------------------------------------------------------- template <> struct pixel_traits<hsi_pixel> { const static bool rgb = false; const static bool rgb_alpha = false; const static bool grayscale = false; const static bool hsi = true; const static long num = 3; static unsigned long max() { return 255;} const static bool has_alpha = false; }; // ---------------------------------------------------------------------------------------- template <typename T> struct grayscale_pixel_traits { const static bool rgb = false; const static bool rgb_alpha = false; const static bool grayscale = true; const static bool hsi = false; const static long num = 1; const static bool has_alpha = false; static unsigned long max() { return std::numeric_limits<T>::max();} }; template <> struct pixel_traits<unsigned char> : public grayscale_pixel_traits<unsigned char> {}; template <> struct pixel_traits<unsigned short> : public grayscale_pixel_traits<unsigned short> {}; template <> struct pixel_traits<unsigned int> : public grayscale_pixel_traits<unsigned int> {}; template <> struct pixel_traits<unsigned long> : public grayscale_pixel_traits<unsigned long> {}; // ---------------------------------------------------------------------------------------- // The following is a bunch of conversion stuff for the assign_pixel function. namespace assign_pixel_helpers { enum { grayscale = 1, rgb, hsi, rgb_alpha }; template < typename P1, typename P2, int p1_type = static_switch< pixel_traits<P1>::grayscale, pixel_traits<P1>::rgb, pixel_traits<P1>::hsi, pixel_traits<P1>::rgb_alpha >::value, int p2_type = static_switch< pixel_traits<P2>::grayscale, pixel_traits<P2>::rgb, pixel_traits<P2>::hsi, pixel_traits<P2>::rgb_alpha >::value > struct helper; // ----------------------------- // all the same kind template < typename P > struct helper<P,P,grayscale,grayscale> { static void assign(P& dest, const P& src) { dest = src; } }; template < typename P1, typename P2 > struct helper<P1,P2,grayscale,grayscale> { static void assign(P1& dest, const P2& src) { if (src <= pixel_traits<P1>::max()) dest = static_cast<P1>(src); else dest = static_cast<P1>(pixel_traits<P1>::max()); } }; template < typename P1, typename P2 > struct helper<P1,P2,rgb,rgb> { static void assign(P1& dest, const P2& src) { dest.red = src.red; dest.green = src.green; dest.blue = src.blue; } }; template < typename P1, typename P2 > struct helper<P1,P2,rgb_alpha,rgb_alpha> { static void assign(P1& dest, const P2& src) { dest.red = src.red; dest.green = src.green; dest.blue = src.blue; dest.alpha = src.alpha; } }; template < typename P1, typename P2 > struct helper<P1,P2,hsi,hsi> { static void assign(P1& dest, const P2& src) { dest.h = src.h; dest.s = src.s; dest.i = src.i; } }; // ----------------------------- // dest is a grayscale template < typename P1, typename P2 > struct helper<P1,P2,grayscale,rgb> { static void assign(P1& dest, const P2& src) { dest = static_cast<P1>((static_cast<unsigned int>(src.red) + static_cast<unsigned int>(src.green) + static_cast<unsigned int>(src.blue))/3); } }; template < typename P1, typename P2 > struct helper<P1,P2,grayscale,rgb_alpha> { static void assign(P1& dest, const P2& src) { const unsigned char avg = static_cast<unsigned char>((static_cast<unsigned int>(src.red) + static_cast<unsigned int>(src.green) + static_cast<unsigned int>(src.blue))/3); if (src.alpha == 255) { dest = avg; } else { // perform this assignment using fixed point arithmetic: // dest = src*(alpha/255) + src*(1 - alpha/255); // dest = src*(alpha/255) + dest*1 - dest*(alpha/255); // dest = dest*1 + src*(alpha/255) - dest*(alpha/255); // dest = dest*1 + (src - dest)*(alpha/255); // dest += (src - dest)*(alpha/255); unsigned int temp = avg; temp -= dest; temp *= src.alpha; temp >>= 8; dest += static_cast<unsigned char>(temp&0xFF); } } }; template < typename P1, typename P2 > struct helper<P1,P2,grayscale,hsi> { static void assign(P1& dest, const P2& src) { dest = static_cast<P1>(src.i); } }; // ----------------------------- struct HSL { double h; double s; double l; }; struct COLOUR { double r; double g; double b; }; /* Calculate HSL from RGB Hue is in degrees Lightness is between 0 and 1 Saturation is between 0 and 1 */ HSL RGB2HSL(COLOUR c1); /* Calculate RGB from HSL, reverse of RGB2HSL() Hue is in degrees Lightness is between 0 and 1 Saturation is between 0 and 1 */ COLOUR HSL2RGB(HSL c1); // ----------------------------- // dest is a color rgb_pixel template < typename P1 > struct helper<P1,unsigned char,rgb,grayscale> { static void assign(P1& dest, const unsigned char& src) { dest.red = src; dest.green = src; dest.blue = src; } }; template < typename P1, typename P2 > struct helper<P1,P2,rgb,grayscale> { static void assign(P1& dest, const P2& src) { unsigned char p; if (src <= 255) p = static_cast<unsigned char>(src); else p = 255; dest.red = p; dest.green = p; dest.blue = p; } }; template < typename P1, typename P2 > struct helper<P1,P2,rgb,rgb_alpha> { static void assign(P1& dest, const P2& src) { if (src.alpha == 255) { dest.red = src.red; dest.green = src.green; dest.blue = src.blue; } else { // perform this assignment using fixed point arithmetic: // dest = src*(alpha/255) + src*(1 - alpha/255); // dest = src*(alpha/255) + dest*1 - dest*(alpha/255); // dest = dest*1 + src*(alpha/255) - dest*(alpha/255); // dest = dest*1 + (src - dest)*(alpha/255); // dest += (src - dest)*(alpha/255); unsigned int temp_r = src.red; unsigned int temp_g = src.green; unsigned int temp_b = src.blue; temp_r -= dest.red; temp_g -= dest.green; temp_b -= dest.blue; temp_r *= src.alpha; temp_g *= src.alpha; temp_b *= src.alpha; temp_r >>= 8; temp_g >>= 8; temp_b >>= 8; dest.red += static_cast<unsigned char>(temp_r&0xFF); dest.green += static_cast<unsigned char>(temp_g&0xFF); dest.blue += static_cast<unsigned char>(temp_b&0xFF); } } }; template < typename P1, typename P2 > struct helper<P1,P2,rgb,hsi> { static void assign(P1& dest, const P2& src) { COLOUR c; HSL h; h.h = src.h; h.h = h.h/255.0*360; h.s = src.s/255.0; h.l = src.i/255.0; c = HSL2RGB(h); dest.red = static_cast<unsigned char>(c.r*255.0); dest.green = static_cast<unsigned char>(c.g*255.0); dest.blue = static_cast<unsigned char>(c.b*255.0); } }; // ----------------------------- // dest is a color rgb_alpha_pixel template < typename P1 > struct helper<P1,unsigned char,rgb_alpha,grayscale> { static void assign(P1& dest, const unsigned char& src) { dest.red = src; dest.green = src; dest.blue = src; dest.alpha = 255; } }; template < typename P1, typename P2 > struct helper<P1,P2,rgb_alpha,grayscale> { static void assign(P1& dest, const P2& src) { unsigned char p; if (src <= 255) p = static_cast<unsigned char>(src); else p = 255; dest.red = p; dest.green = p; dest.blue = p; dest.alpha = 255; } }; template < typename P1, typename P2 > struct helper<P1,P2,rgb_alpha,rgb> { static void assign(P1& dest, const P2& src) { dest.red = src.red; dest.green = src.green; dest.blue = src.blue; dest.alpha = 255; } }; template < typename P1, typename P2 > struct helper<P1,P2,rgb_alpha,hsi> { static void assign(P1& dest, const P2& src) { COLOUR c; HSL h; h.h = src.h; h.h = h.h/255.0*360; h.s = src.s/255.0; h.l = src.i/255.0; c = HSL2RGB(h); dest.red = static_cast<unsigned char>(c.r*255.0); dest.green = static_cast<unsigned char>(c.g*255.0); dest.blue = static_cast<unsigned char>(c.b*255.0); dest.alpha = 255; } }; // ----------------------------- // dest is an hsi pixel template < typename P1> struct helper<P1,unsigned char,hsi,grayscale> { static void assign(P1& dest, const unsigned char& src) { dest.h = 0; dest.s = 0; dest.i = src; } }; template < typename P1, typename P2 > struct helper<P1,P2,hsi,grayscale> { static void assign(P1& dest, const P2& src) { dest.h = 0; dest.s = 0; if (src <= 255) dest.i = static_cast<unsigned char>(src); else dest.i = 255; } }; template < typename P1, typename P2 > struct helper<P1,P2,hsi,rgb_alpha> { static void assign(P1& dest, const P2& src) { rgb_pixel temp; // convert target hsi pixel to rgb helper<rgb_pixel,P1>::assign(temp,dest); // now assign the rgb_alpha value to our temp rgb pixel helper<rgb_pixel,P2>::assign(temp,src); // now we can just go assign the new rgb value to the // hsi pixel helper<P1,rgb_pixel>::assign(dest,temp); } }; template < typename P1, typename P2 > struct helper<P1,P2,hsi,rgb> { static void assign(P1& dest, const P2& src) { COLOUR c1; HSL c2; c1.r = src.red/255.0; c1.g = src.green/255.0; c1.b = src.blue/255.0; c2 = RGB2HSL(c1); dest.h = static_cast<unsigned char>(c2.h/360.0*255.0); dest.s = static_cast<unsigned char>(c2.s*255.0); dest.i = static_cast<unsigned char>(c2.l*255.0); } }; } // ----------------------------- template < typename P1, typename P2 > inline void assign_pixel ( P1& dest, const P2& src ) { assign_pixel_helpers::helper<P1,P2>::assign(dest,src); } template < typename P1> inline void assign_pixel ( P1& dest, const int src ) { unsigned long p; if (src >= 0) p = static_cast<unsigned long>(src); else p = 0; assign_pixel(dest, p); } template < typename P1> inline void assign_pixel ( P1& dest, const long src ) { unsigned long p; if (src >= 0) p = static_cast<unsigned long>(src); else p = 0; assign_pixel(dest, p); } // ---------------------------------------------------------------------------------------- template < typename P > inline typename enable_if_c<pixel_traits<P>::grayscale>::type assign_pixel_intensity_helper ( P& dest, const unsigned long& new_intensity ) { assign_pixel(dest, new_intensity); } template < typename P > inline typename enable_if_c<pixel_traits<P>::grayscale == false&& pixel_traits<P>::has_alpha>::type assign_pixel_intensity_helper ( P& dest, const unsigned long& new_intensity ) { unsigned long alpha = dest.alpha; hsi_pixel p; assign_pixel(p,dest); assign_pixel(p.i, new_intensity); assign_pixel(dest,p); dest.alpha = alpha; } template < typename P > inline typename enable_if_c<pixel_traits<P>::grayscale == false&& pixel_traits<P>::has_alpha == false>::type assign_pixel_intensity_helper ( P& dest, const unsigned long& new_intensity ) { hsi_pixel p; assign_pixel(p,dest); assign_pixel(p.i, new_intensity); assign_pixel(dest,p); } template < typename P > inline void assign_pixel_intensity ( P& dest, const unsigned long new_intensity ) { assign_pixel_intensity_helper(dest, new_intensity); } // ---------------------------------------------------------------------------------------- template < typename P > inline typename enable_if_c<pixel_traits<P>::grayscale,unsigned long>::type get_pixel_intensity_helper ( const P& src ) { return static_cast<unsigned long>(src); } template < typename P > inline typename enable_if_c<pixel_traits<P>::grayscale == false&& pixel_traits<P>::has_alpha, unsigned long>::type get_pixel_intensity_helper ( const P& src ) { P temp = src; temp.alpha = 255; hsi_pixel p; assign_pixel(p,temp); return static_cast<unsigned long>(p.i); } template < typename P > inline typename enable_if_c<pixel_traits<P>::grayscale == false&& pixel_traits<P>::has_alpha == false, unsigned long>::type get_pixel_intensity_helper ( const P& src ) { hsi_pixel p; assign_pixel(p,src); return static_cast<unsigned long>(p.i); } template < typename P > inline unsigned long get_pixel_intensity ( const P& src ) { return get_pixel_intensity_helper(src); } // ---------------------------------------------------------------------------------------- } #ifdef NO_MAKEFILE #include "pixel.cpp" #endif #endif // DLIB_PIXEl_