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