dlib C++ Library - pixel.h

    // 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_