Generic Image Library (GIL) and floating-point types

I am trying to use GIL with the OpenEXR library, which uses half-precision floating-point channels: typedef image_type<half, rgb_t>::type rgb16f_image_t; typedef pixel<half, rgb_t> rgb16f_pixel_t; ... which works fine. However, I also need to convert to 8bpp integer images for use with a UI toolkit: rgb16f_image_t a(100, 100); rgb8_image_t b(100, 100); copy_pixels(const_view(a), view(b)); After providing a specialization for PixelsCompatibleConcept the code compiles fine, but I would like to provide my own code for converting channels of type "half" to type "bits8" (e.g: to convert values in the range [0, 1] to [0, 255]). Any thoughts? Tim Shead

Hi Tim, We don't recommend that you treat half as compatible with bits8. In general, GIL does not allow standard copying using copy_pixels, operator=, etc. between pixels that are incompatible. For pixels to be compatible they must have the same base color space and channel types. (See PixelsCompatibleConcept) In other words, you must be able to convert back and forth between them with no loss of precision. In your case you want to perform a lossy conversion between half and bits8. You have to do it via color conversion, which is allowed to be lossy. To do that you must make half model ChannelConvertibleConcept. Here is how to do what you want: namespace gil { template <> inline bits8 _channel_convert<bits8>(half h) { return bits8(h*255); } } boost::function_requires<ChannelConvertibleConcept<half,bits32f> >(); rgb16f_image_t a(100, 100); rgb8_image_t b(100, 100); copy_and_convert_pixels(const_view(a), view(b)); Lubomir
-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Timothy M. Shead Sent: Monday, October 16, 2006 8:14 PM To: boost@lists.boost.org Subject: [boost] Generic Image Library (GIL) and floating-point types
I am trying to use GIL with the OpenEXR library, which uses half-precision floating-point channels:
typedef image_type<half, rgb_t>::type rgb16f_image_t; typedef pixel<half, rgb_t> rgb16f_pixel_t;
... which works fine. However, I also need to convert to 8bpp integer images for use with a UI toolkit:
rgb16f_image_t a(100, 100); rgb8_image_t b(100, 100); copy_pixels(const_view(a), view(b));
After providing a specialization for PixelsCompatibleConcept the code compiles fine, but I would like to provide my own code for converting channels of type "half" to type "bits8" (e.g: to convert values in the range [0, 1] to [0, 255]).
Any thoughts?
Tim Shead
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Lubomir: I appreciate the fast response, your suggestion worked like a charm, so I'll take the example a step further - suppose that I actually want to convert floating-point RGBA data to 8bpp integer RGB (I'm going to display the alpha separately, say). The current default policy for conversion from RGBA to RGB is to multiply the RGB channels by the alpha channel, which is inappropriate in this case - I just want to extract RGB and ignore the alpha. I can override the default by creating my own specialization: namespace gil { template <typename T1, typename T2> struct color_converter_default_impl<T1,rgba_t,T2,rgb_t> { template <typename P1, typename P2> void operator()(const P1& src, P2& dst) const { dst.red = channel_convert<T2>(src.red); dst.green = channel_convert<T2>(src.green); dst.blue = channel_convert<T2>(src.blue); } }; } // namespace gil ... but this is a fairly intrusive, blanket replacement of the default. How would you handle this on a case-by-case basis? I'd liken it to providing custom functors for an STL algorithm. Regards, Tim Shead On Mon, 2006-10-16 at 22:21 -0700, Lubomir Bourdev wrote:
Hi Tim,
We don't recommend that you treat half as compatible with bits8.
In general, GIL does not allow standard copying using copy_pixels, operator=, etc. between pixels that are incompatible. For pixels to be compatible they must have the same base color space and channel types. (See PixelsCompatibleConcept) In other words, you must be able to convert back and forth between them with no loss of precision.
In your case you want to perform a lossy conversion between half and bits8. You have to do it via color conversion, which is allowed to be lossy. To do that you must make half model ChannelConvertibleConcept. Here is how to do what you want:
namespace gil { template <> inline bits8 _channel_convert<bits8>(half h) { return bits8(h*255); } }
boost::function_requires<ChannelConvertibleConcept<half,bits32f> >(); rgb16f_image_t a(100, 100); rgb8_image_t b(100, 100); copy_and_convert_pixels(const_view(a), view(b));
Lubomir
-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Timothy M. Shead Sent: Monday, October 16, 2006 8:14 PM To: boost@lists.boost.org Subject: [boost] Generic Image Library (GIL) and floating-point types
I am trying to use GIL with the OpenEXR library, which uses half-precision floating-point channels:
typedef image_type<half, rgb_t>::type rgb16f_image_t; typedef pixel<half, rgb_t> rgb16f_pixel_t;
... which works fine. However, I also need to convert to 8bpp integer images for use with a UI toolkit:
rgb16f_image_t a(100, 100); rgb8_image_t b(100, 100); copy_pixels(const_view(a), view(b));
After providing a specialization for PixelsCompatibleConcept the code compiles fine, but I would like to provide my own code for converting channels of type "half" to type "bits8" (e.g: to convert values in the range [0, 1] to [0, 255]).
Any thoughts?
Tim Shead
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Tim Shead wrote:
Suppose that I actually want to convert floating-point RGBA data to 8bpp integer RGB (I'm going to display the alpha separately, say). ... <skipped> How would you handle this on a case-by-case basis?
Hi Tim, This is described in Section 16 in the design guide. You can create your own color converters with arbitrary conversion policy. If you want to change the color conversion in only a few cases, you simply subclass GIL's default color converter. Color conversion functions take an optional color conversion object. Pass your converter there. Lubomir _________________________ // make the default use GIL's default template <typename T1, typename C1, // source channel type and base color space typename T2, typename C2> // destination channel type and base color space struct my_color_converter_impl : public color_converter_default_impl<T1,C1,T2,C2> { }; // provide specializations only for cases you care about template <typename T1, typename T2> struct my_color_converter_impl<T1,rgba_t,T2,rgb_t> { template <typename P1, typename P2> void operator()(const P1& src, P2& dst) const { dst.red = channel_convert<T2>(src.red); dst.green = channel_convert<T2>(src.green); dst.blue = channel_convert<T2>(src.blue); } }; // create a color converter object that dispatches to your own implementation struct my_color_converter { template <typename SrcP,typename DstP> void operator()(const SrcP& src,DstP& dst) const { typedef typename pixel_traits<SrcP>::channel_t SrcChannel; typedef typename pixel_traits<DstP>::channel_t DstChannel; typedef typename pixel_traits<SrcP>::color_space_t::base SrcColorSpace; typedef typename pixel_traits<DstP>::color_space_t::base DstColorSpace; my_color_converter_impl<SrcChannel,SrcColorSpace,DstChannel,DstColorSpac e>()(src,dst); } }; int main(int argc, unsigned char* argv[]) { rgba8_image_t a(100, 100); rgb8_image_t b(100, 100); copy_and_convert_pixels(const_view(a), view(b), my_color_converter()); }

"Timothy M. Shead" <tshead@k-3d.com> wrote in message news:1161054851.13317.27.camel@joe.k-3d.com...
I am trying to use GIL with the OpenEXR library, which uses half-precision floating-point channels:
typedef image_type<half, rgb_t>::type rgb16f_image_t; typedef pixel<half, rgb_t> rgb16f_pixel_t;
... which works fine. However, I also need to convert to 8bpp integer images for use with a UI toolkit:
rgb16f_image_t a(100, 100); rgb8_image_t b(100, 100); copy_pixels(const_view(a), view(b));
After providing a specialization for PixelsCompatibleConcept the code compiles fine, but I would like to provide my own code for converting channels of type "half" to type "bits8" (e.g: to convert values in the range [0, 1] to [0, 255]).
Any thoughts?
Tim Shead
I'm not sure if the following fits the bill , but it might provide some ideas. (quan arithmetic_promote header is : http://quan.cvs.sourceforge.net/quan/quan-trunk/quan/meta/arithmetic_promote... which has no dependencies afaics. Alternatively try Boost.Typeof) have fun... regards Andy Little ------------------ #include <boost/numeric/conversion/converter.hpp> #include <quan/meta/arithmetic_promote.hpp> // rgb_value includes N as its normalisation to 1 value //where normalised = value/N template< typename T, unsigned N
struct rgb_value{ typedef T value_type; static const unsigned max_rgb_value = N; T value; explicit rgb_value(T const & in) : value(in){} rgb_value() : value(T(0)){} }; template < typename T, template <typename , typename > class Policy,// intermediate calc policy typename S
T rgb_conv(S const & from) { typedef Policy<typename T::value_type,typename S::value_type>::type Inter; Inter inter = (static_cast<Inter>(from.value) * T::max_rgb_value )/ S::max_rgb_value; // Boost.Numeric.Converter set up with nearest neighbour rounding typedef typename boost::numeric::converter< typename T::value_type, Inter, boost::numeric::conversion_traits<typename T::value_type,Inter>, boost::numeric::def_overflow_handler, // boost::numeric::Trunc<Inter> boost::numeric::RoundEven<Inter> > converter; typename T::value_type result_value = converter()(inter); T result(result_value); return result; } // intermediate calc policy template <typename T1, typename T2> struct double_policy{ typedef double type; }; #include <iostream> int main() { typedef rgb_value<double,1> rgb_double; typedef rgb_value<float,1> rgb_float; typedef rgb_value<unsigned char,0xFF> rgb_int8; typedef rgb_value<unsigned int,0xFFFF> rgb_int16; typedef rgb_value<unsigned int,0xFFFFFFFF> rgb_int32; using quan::meta::arithmetic_promote; rgb_double v1(.3); std::cout << "rgb_double value = " << v1.value <<'\n'; rgb_float v1a = rgb_conv<rgb_float,arithmetic_promote>(v1); std::cout << "rgb_double value as float = " << v1a.value <<'\n'; rgb_int8 v2 = rgb_conv<rgb_int8,arithmetic_promote>(v1); std::cout << "rgb_double as rgb8 value = "<< std::hex << (unsigned int) v2.value <<'\n'; rgb_int16 v3 = rgb_conv<rgb_int16,arithmetic_promote>(v1); std::cout << "rgb_double as rgb16 value = " << std::hex << v3.value <<'\n'; rgb_int32 v4 = rgb_conv<rgb_int32,arithmetic_promote>(v1); std::cout << "rgb_double as rgb32 value = "<< std::hex << v4.value <<'\n'; v1 = rgb_conv<rgb_double,arithmetic_promote>(v2); v4 = rgb_conv<rgb_int32,double_policy>(v2); std::cout << "rgb_int8 as rgb32 value = "<< std::hex << v4.value <<'\n'; v2 = rgb_conv<rgb_int8,double_policy>(v4); std::cout << "rgb_int32 as rgb8 value = "<< std::hex << (unsigned int) v2.value <<'\n'; v1 = rgb_conv<rgb_double,arithmetic_promote>(v2); std::cout << "rgb_int8 back to rgb double " << v1.value <<'\n'; };

On Wed, 2006-10-18 at 12:29 +0100, Andy Little wrote:
"Timothy M. Shead" <tshead@k-3d.com> wrote in message news:1161054851.13317.27.camel@joe.k-3d.com...
I am trying to use GIL with the OpenEXR library, which uses half-precision floating-point channels:
I'm not sure if the following fits the bill , but it might provide some ideas.
(quan arithmetic_promote header is :
http://quan.cvs.sourceforge.net/quan/quan-trunk/quan/meta/arithmetic_promote...
Actually, you may have missed Lubomir's response to my message, which - in a nutshell - is to provide a specialization for conversion between the new and existing types: namespace gil { /// Defines conversion from 16-bit-floats to 8-bit-integers template<> inline bits8 _channel_convert<bits8>(half Value) { return bits8(Value * 255); } /// Defines conversion from 8-bit-integers to 16-bit-floats template<> inline half _channel_convert<half>(bits8 Value) { return half(Value) / 255.0; } } // namespace gil Cheers, Tim

"Timothy M. Shead" <tshead@k-3d.com> wrote in message news:1161178677.13317.51.camel@joe.k-3d.com...
On Wed, 2006-10-18 at 12:29 +0100, Andy Little wrote:
"Timothy M. Shead" <tshead@k-3d.com> wrote in message news:1161054851.13317.27.camel@joe.k-3d.com...
I am trying to use GIL with the OpenEXR library, which uses half-precision floating-point channels:
I'm not sure if the following fits the bill , but it might provide some ideas.
(quan arithmetic_promote header is :
http://quan.cvs.sourceforge.net/quan/quan-trunk/quan/meta/arithmetic_promote...
Actually, you may have missed Lubomir's response to my message, which - in a nutshell - is to provide a specialization for conversion between the new and existing types:
Oh yes I missed it, sorry.
namespace gil {
/// Defines conversion from 16-bit-floats to 8-bit-integers template<> inline bits8 _channel_convert<bits8>(half Value) { return bits8(Value * 255); }
/// Defines conversion from 8-bit-integers to 16-bit-floats template<> inline half _channel_convert<half>(bits8 Value) { return half(Value) / 255.0; }
} // namespace gil
That looks like a lot less work, much better. regards Andy little
participants (3)
-
Andy Little
-
Lubomir Bourdev
-
Timothy M. Shead