From 1b9bcde35a90c1de40bd52a58900d3f8ef24a4fb Mon Sep 17 00:00:00 2001 From: cnr-isti-vclab Date: Tue, 14 Jul 2009 14:43:19 +0000 Subject: [PATCH] added img module --- img/img.h | 18 ++ img/img_attributes.h | 246 +++++++++++++++++++++ img/img_base.h | 69 ++++++ img/img_convert.h | 169 ++++++++++++++ img/img_cs_base.h | 248 +++++++++++++++++++++ img/img_filter.h | 388 ++++++++++++++++++++++++++++++++ img/img_image.h | 512 +++++++++++++++++++++++++++++++++++++++++++ img/img_info.h | 45 ++++ img/img_io.h | 84 +++++++ img/img_scalar.h | 108 +++++++++ 10 files changed, 1887 insertions(+) create mode 100755 img/img.h create mode 100644 img/img_attributes.h create mode 100755 img/img_base.h create mode 100755 img/img_convert.h create mode 100644 img/img_cs_base.h create mode 100755 img/img_filter.h create mode 100755 img/img_image.h create mode 100755 img/img_info.h create mode 100755 img/img_io.h create mode 100755 img/img_scalar.h diff --git a/img/img.h b/img/img.h new file mode 100755 index 00000000..c79c5d94 --- /dev/null +++ b/img/img.h @@ -0,0 +1,18 @@ +#ifndef IMG_INCLUDES_H_ +#define IMG_INCLUDES_H_ +/*! \file img.h + \brief includes all img headers + + This header just includes every img module header. +*/ + +#include "img/img_base.h" +#include "img_scalar.h" +#include "img/img_image.h" +#include "img_convert.h" +#include "img_cs_base.h" +#include "img_io.h" +#include "img_info.h" +#include "img_filter.h" + +#endif /*IMG_INCLUDES_H_*/ diff --git a/img/img_attributes.h b/img/img_attributes.h new file mode 100644 index 00000000..15925426 --- /dev/null +++ b/img/img_attributes.h @@ -0,0 +1,246 @@ +#ifndef IMG_ATTRIBUTES_H_ +#define IMG_ATTRIBUTES_H_ + +#include "img/img_base.h" + +namespace img { + + enum COLORSPACE { + UNDEFINED, + RGB, + SRGB, + CIE_XYZ, + CIE_LAB, + CIE_LUV, + }; + +template +class ImgAttributes +{ +public: + +private: + COLORSPACE _colorspace; + + ScalarType _gamma; + + ScalarType _range_min; + ScalarType _range_max; + + ScalarType _reference_white_x; + ScalarType _reference_white_y; + ScalarType _reference_white_z; + +public: + ImgAttributes() + : _colorspace(UNDEFINED), _gamma(ScalarType(0.0)), + _range_min(ScalarType(0.0)), _range_max(ScalarType(0.0)), + _reference_white_x(ScalarType(0.0)), _reference_white_y(ScalarType(0.0)), _reference_white_z(ScalarType(0.0)) + { + STATIC_FLOAT_OR_DOUBLE_TYPECHECK( ScalarType ); + } + + ImgAttributes(const ImgAttributes &attributes) + { + STATIC_FLOAT_OR_DOUBLE_TYPECHECK( ScalarType ); + _colorspace = attributes._colorspace; + _gamma = attributes._gamma; + _range_min = attributes._range_min; + _range_max = attributes._range_max; + _reference_white_x = attributes._reference_white_x; + _reference_white_y = attributes._reference_white_y; + _reference_white_z = attributes._reference_white_z; + } + + template + ImgAttributes(const ImgAttributes &attributes) + { + STATIC_FLOAT_OR_DOUBLE_TYPECHECK( ScalarType ); + _colorspace = attributes._colorspace; + _gamma = static_cast(attributes._gamma); + _range_min = static_cast(attributes._range_min); + _range_max = static_cast(attributes._range_max); + _reference_white_x = static_cast(attributes._reference_white_x); + _reference_white_y = static_cast(attributes._reference_white_y); + _reference_white_z = static_cast(attributes._reference_white_z); + } + + template + inline ImgAttributes< ScalarType> & operator =(const ImgAttributes &attributes) + { + _colorspace = attributes._colorspace; + _gamma = static_cast(attributes._gamma); + _range_min = static_cast(attributes._range_min); + _range_max = static_cast(attributes._range_max); + _reference_white_x = static_cast(attributes._reference_white_x); + _reference_white_y = static_cast(attributes._reference_white_y); + _reference_white_z = static_cast(attributes._reference_white_z); + } + + inline void reset() + { + _colorspace = UNDEFINED; + _gamma = ScalarType(0.0); + _range_min = ScalarType(0.0); + _range_max = ScalarType(0.0); + _reference_white_x = ScalarType(0.0); + _reference_white_y = ScalarType(0.0); + _reference_white_z = ScalarType(0.0); + } + + // getters + void getColorspace(COLORSPACE &ret_colorspace) const + { + ret_colorspace = _colorspace; + } + + template + void getGamma(OtherScalarType &ret_gamma) const + { + STATIC_FLOAT_OR_DOUBLE_TYPECHECK( OtherScalarType ); + ret_gamma = static_cast(_gamma); + } + + template + void getRange(OtherScalarType &ret_range_min, OtherScalarType &ret_range_max) const + { + STATIC_FLOAT_OR_DOUBLE_TYPECHECK( OtherScalarType ); + ret_range_min = static_cast(_range_min); + ret_range_max = static_cast(_range_max); + } + + template + void getReferenceWhite(OtherScalarType &ret_reference_white_x, OtherScalarType &ret_reference_white_y, OtherScalarType &ret_reference_white_z) const + { + STATIC_FLOAT_OR_DOUBLE_TYPECHECK( OtherScalarType ); + ret_reference_white_x = static_cast(_reference_white_x); + ret_reference_white_y = static_cast(_reference_white_y); + ret_reference_white_z = static_cast(_reference_white_z); + } + + // setters + void setColorspace(COLORSPACE arg_colorspace) + { + _colorspace = arg_colorspace; + } + + void setGamma(ScalarType arg_gamma) + { + _gamma = arg_gamma; + } + + void setRange(ScalarType arg_range_min, ScalarType arg_range_max) + { + assert(arg_range_min<=arg_range_max); + _range_min = arg_range_min; + _range_max = arg_range_max; + } + + void setReferenceWhite(ScalarType arg_reference_white_x, ScalarType arg_reference_white_y, ScalarType arg_reference_white_z) + { + _reference_white_x = arg_reference_white_x; + _reference_white_y = arg_reference_white_y; + _reference_white_z = arg_reference_white_z; + } + + void setColorspace(const ImgAttributes &attributes) + { + _colorspace = attributes._colorspace; + } + + void setGamma(const ImgAttributes &attributes) + { + _gamma = attributes._gamma; + } + + void setRange(const ImgAttributes &attributes) + { + _range_min = attributes._range_min; + _range_max = attributes._range_max; + } + + void setReferenceWhite(const ImgAttributes &attributes) + { + _reference_white_x = attributes._reference_white_x; + _reference_white_y = attributes._reference_white_y; + _reference_white_z = attributes._reference_white_z; + } + + // checks + bool hasColorspace(COLORSPACE arg_colorspace) const + { + return _colorspace == arg_colorspace; + } + + bool hasGamma(ScalarType arg_gamma) const + { + return _gamma == arg_gamma; + } + + + bool hasRange(ScalarType arg_range_min, ScalarType arg_range_max) const + { + return (_range_min == arg_range_min) && + (_range_max == arg_range_max); + } + + bool hasReferenceWhite(ScalarType arg_reference_white_x, ScalarType arg_reference_white_y, ScalarType arg_reference_white_z) const + { + return (_reference_white_x == arg_reference_white_x) && + (_reference_white_y == arg_reference_white_y) && + (_reference_white_z == arg_reference_white_z); + } + + bool hasColorspace(const ImgAttributes &attributes) const + { + return _colorspace == attributes._colorspace; + } + + bool hasGamma(const ImgAttributes &attributes) const + { + return _gamma == attributes._gamma; + } + + bool hasRange(const ImgAttributes &attributes) const + { + return (_range_min == attributes._range_min) && + (_range_max == attributes._range_max); + } + + bool hasReferenceWhite(const ImgAttributes &attributes) const + { + return (_reference_white_x == attributes._reference_white_x) && + (_reference_white_y == attributes._reference_white_y) && + (_reference_white_z == attributes._reference_white_z); + } + + + bool operator==(const ImgAttributes &attributes) const + { + return hasColorspace(attributes) && + hasGamma(attributes) && + hasRange(attributes) && + hasReferenceWhite(attributes); + } + + static ImgAttributes createImgAttributes(COLORSPACE arg_colorspace = ScalarType(UNDEFINED), + ScalarType arg_gamma = ScalarType(0.0), + ScalarType arg_range_min = ScalarType(0.0), + ScalarType arg_range_max = ScalarType(0.0), + ScalarType arg_reference_white_x = ScalarType(0.0), + ScalarType arg_reference_white_y = ScalarType(0.0), + ScalarType arg_reference_white_z = ScalarType(0.0)) + { + ImgAttributes attributes; + attributes.setColorspace(arg_colorspace); + attributes.setGamma(arg_gamma); + attributes.setRange(arg_range_min, arg_range_max); + attributes.setReferenceWhite(arg_reference_white_x, arg_reference_white_y, arg_reference_white_z); + return attributes; + } + +}; + +} // end namespace img + +#endif /*IMG_ATTRIBUTES_H_*/ diff --git a/img/img_base.h b/img/img_base.h new file mode 100755 index 00000000..aa773036 --- /dev/null +++ b/img/img_base.h @@ -0,0 +1,69 @@ +#ifndef IMG_BASE_H_ +#define IMG_BASE_H_ +/*! \file img_base.h + \brief basic definitions for the img module + + This header contains the basic module definitions. +*/ + +/// base of the static assertion mechanism +template struct NON_TRUE_EXPR_CompileTimeError; +/// partial instantiation for the static assertion mechanism +template<> struct NON_TRUE_EXPR_CompileTimeError {}; + +/// the static assertion mechanism +#define STATIC_ASSERT(exp) (NON_TRUE_EXPR_CompileTimeError< (exp) >()) + +/// base of the static typecheck mechanism +template struct NON_FLOAT_OR_DOUBLE_TYPE_CompileTimeError; +/// partial instantiation for the static typecheck mechanism +template<> struct NON_FLOAT_OR_DOUBLE_TYPE_CompileTimeError {}; +/// partial instantiation for the static typecheck mechanism +template<> struct NON_FLOAT_OR_DOUBLE_TYPE_CompileTimeError {}; + +/// the static typecheck mechanism +#define STATIC_FLOAT_OR_DOUBLE_TYPECHECK(type) (NON_FLOAT_OR_DOUBLE_TYPE_CompileTimeError< type >()) + +/// define NULL pointer value +#ifndef NULL + #ifdef __cplusplus + #define NULL 0 + #else + #define NULL ((void *)0) + #endif +#endif + +#include +#include +#include +#include + +/*! \brief the img module namespace + + this is the main image module namespace. +*/ +namespace img { + +/*! \brief the basic exception class + + this is the basic image exception class, it simply carries an error string to the console. +*/ +class ImageException: public std::exception +{ +public: + /// the error string + const char *message; + /// default constructor + ImageException():exception(),message("no message"){} + /*! \brief message carrying constructor + + \param arg_message the error string + */ + ImageException(const char *arg_message):exception(),message(arg_message){} + /// the destructor + virtual ~ImageException () throw (){} +}; + +} //end namespace img + +#endif /*IMG_BASE_H_*/ diff --git a/img/img_convert.h b/img/img_convert.h new file mode 100755 index 00000000..b4a27230 --- /dev/null +++ b/img/img_convert.h @@ -0,0 +1,169 @@ +#ifndef IMG_CONVERT_H_ +#define IMG_CONVERT_H_ + +// implementation of conversions between basic image types +// assumes linearity for both source and destination +// uses CIE 1931 luminance definition for default grayscale conversion: +// Y = 0.212671*R + 0.715160*G + 0.072169*B +// assumes 255 for default alpha_value parameter + +#include "img/img_image.h" + +namespace img { + +// Y to RGB +template +inline void convert_Y_to_RGB(const Image<1,SrcScalarType,SrcSafe> &source, Image<3,DestScalarType,DestSafe> &destination) +{ + assert(source.isValid()); + if(SrcSafe || DestSafe){ + if(!source.isValid()) throw ImageException("Invalid source image"); + } + destination.setZero(source.width(),source.height()); + for (int y_coord = 0; y_coord < source.height(); ++y_coord) + for (int x_coord = 0; x_coord < source.width(); ++x_coord){ + DestScalarType Y = static_cast(source.getValue(x_coord, y_coord, 0)); + destination.setValue(x_coord, y_coord, 0, Y); + destination.setValue(x_coord, y_coord, 1, Y); + destination.setValue(x_coord, y_coord, 2, Y); + } + destination.attributes.setRange(source.attributes); + destination.attributes.setGamma(source.attributes); +} + +// Y to RGBA +template +inline void convert_Y_to_RGBA(const Image<1,SrcScalarType,SrcSafe> &source, Image<4,DestScalarType,DestSafe> &destination, DestScalarType alpha_value=255.0f ) +{ + assert(source.isValid()); + if(SrcSafe || DestSafe){ + if(!source.isValid()) throw ImageException("Invalid source image"); + } + destination.setZero(source.width(),source.height()); + for (int y_coord = 0; y_coord < source.height(); ++y_coord) + for (int x_coord = 0; x_coord < source.width(); ++x_coord){ + DestScalarType Y = static_cast(source.getValue(x_coord, y_coord, 0)); + destination.setValue(x_coord, y_coord, 0, Y); + destination.setValue(x_coord, y_coord, 1, Y); + destination.setValue(x_coord, y_coord, 2, Y); + destination.setValue(x_coord, y_coord, 3, alpha_value); + } + destination.attributes.setRange(source.attributes); + destination.attributes.setGamma(source.attributes); +} + +// RGB to Y +template +inline void convert_RGB_to_Y(const Image<3,SrcScalarType,SrcSafe> &source, Image<1,DestScalarType,DestSafe> &destination) +{ + assert(source.isValid()); + if(SrcSafe || DestSafe){ + if(!source.isValid()) throw ImageException("Invalid source image"); + } + destination.setZero(source.width(),source.height()); + for (int y_coord = 0; y_coord < source.height(); ++y_coord) + for (int x_coord = 0; x_coord < source.width(); ++x_coord){ + DestScalarType R = static_cast(source.getValue(x_coord, y_coord, 0)); + DestScalarType G = static_cast(source.getValue(x_coord, y_coord, 1)); + DestScalarType B = static_cast(source.getValue(x_coord, y_coord, 2)); + destination.setValue(x_coord, y_coord, 0, 0.212671f*R + 0.715160f*G + 0.072169f*B); + } + destination.attributes.setRange(source.attributes); + destination.attributes.setGamma(source.attributes); +} + +// RGB to RGBA +template +inline void convert_RGB_to_RGBA(const Image<3,SrcScalarType,SrcSafe> &source, Image<4,DestScalarType,DestSafe> &destination, DestScalarType alpha_value=255.0f ) +{ + assert(source.isValid()); + if(SrcSafe || DestSafe){ + if(!source.isValid()) throw ImageException("Invalid source image"); + } + destination.setZero(source.width(),source.height()); + for (int y_coord = 0; y_coord < source.height(); ++y_coord) + for (int x_coord = 0; x_coord < source.width(); ++x_coord){ + destination.setValue(x_coord, y_coord, 0, static_cast(source.getValue(x_coord, y_coord, 0))); + destination.setValue(x_coord, y_coord, 1, static_cast(source.getValue(x_coord, y_coord, 1))); + destination.setValue(x_coord, y_coord, 2, static_cast(source.getValue(x_coord, y_coord, 2))); + destination.setValue(x_coord, y_coord, 3, alpha_value); + } + destination.attributes.setRange(source.attributes); + destination.attributes.setGamma(source.attributes); +} + +// RGBA to Y +template +inline void convert_RGBA_to_Y(const Image<4,SrcScalarType,SrcSafe> &source, Image<1,DestScalarType,DestSafe> &destination) +{ + assert(source.isValid()); + if(SrcSafe || DestSafe){ + if(!source.isValid()) throw ImageException("Invalid source image"); + } + destination.setZero(source.width(),source.height()); + for (int y_coord = 0; y_coord < source.height(); ++y_coord) + for (int x_coord = 0; x_coord < source.width(); ++x_coord){ + DestScalarType R = static_cast(source.getValue(x_coord, y_coord, 0)); + DestScalarType G = static_cast(source.getValue(x_coord, y_coord, 1)); + DestScalarType B = static_cast(source.getValue(x_coord, y_coord, 2)); + destination.setValue(x_coord, y_coord, 0, 0.212671f*R + 0.715160f*G + 0.072169f*B); + } + destination.attributes.setRange(source.attributes); + destination.attributes.setGamma(source.attributes); +} + +// RGBA to RGB +template +inline void convert_RGBA_to_RGB(const Image<4,SrcScalarType,SrcSafe> &source, Image<3,DestScalarType,DestSafe> &destination) +{ + assert(source.isValid()); + if(SrcSafe || DestSafe){ + if(!source.isValid()) throw ImageException("Invalid source image"); + } + destination.setZero(source.width(),source.height()); + for (int y_coord = 0; y_coord < source.height(); ++y_coord) + for (int x_coord = 0; x_coord < source.width(); ++x_coord){ + destination.setValue(x_coord, y_coord, 0, static_cast(source.getValue(x_coord, y_coord, 0))); + destination.setValue(x_coord, y_coord, 1, static_cast(source.getValue(x_coord, y_coord, 1))); + destination.setValue(x_coord, y_coord, 2, static_cast(source.getValue(x_coord, y_coord, 2))); + } + destination.attributes.setRange(source.attributes); + destination.attributes.setGamma(source.attributes); +} + +// range conversion +template +inline void convert_range_0_255_to_0_1(const Image &source, Image &destination) +{ + assert(source.isValid()); + assert(source.attributes.hasRange(0,255)); + if(SrcSafe || DestSafe){ + if(!source.isValid()) throw ImageException("Invalid source image"); + if(!source.attributes.hasRange(0,255)) throw ImageException("Invalid range attribute"); + } + destination.setZero(source.width(),source.height()); + for(int offset=0;offset(source.dataValues()[offset]) / DestScalarType(255.0); + destination.attributes=source.attributes; + destination.attributes.setRange(0,1); +} + +template +inline void convert_range_0_1_to_0_255(const Image &source, Image &destination) +{ + assert(source.isValid()); + assert(source.attributes.hasRange(0,1)); + if(SrcSafe || DestSafe){ + if(!source.isValid()) throw ImageException("Invalid source image"); + if(!source.attributes.hasRange(0,1)) throw ImageException("Invalid range attribute"); + } + destination.setZero(source.width(),source.height()); + for(int offset=0;offset(source.dataValues()[offset]) * DestScalarType(255.0); + destination.attributes=source.attributes; + destination.attributes.setRange(0,255); +} + +} //end namespace img + +#endif /*IMG_CONVERT_H_*/ diff --git a/img/img_cs_base.h b/img/img_cs_base.h new file mode 100644 index 00000000..d6f07f6a --- /dev/null +++ b/img/img_cs_base.h @@ -0,0 +1,248 @@ +#ifndef IMG_CS_BASE_H_ +#define IMG_CS_BASE_H_ + +// warning: temporary name/location, will change in near future + +#include "img/img.h" + +namespace img { + +// RGB: GAMMA -> LINEAR +template +inline void convert_gamma_precompensated_rgb_to_linear_rgb(const img::Image &gamma_precompensated_rgb_image, img::Image &linear_rgb_image) +{ + assert(gamma_precompensated_rgb_image.isValid()); + assert(!gamma_precompensated_rgb_image.attributes.hasColorspace(SRGB)); + assert(gamma_precompensated_rgb_image.attributes.hasRange(0.0,1.0)); + ScalarType2 old_gamma; + gamma_precompensated_rgb_image.attributes.getGamma(old_gamma); + assert((old_gamma>0.0)&&(old_gamma<1.0)); + if(Safe1 || Safe2){ + if(!gamma_precompensated_rgb_image.isValid()) throw ImageException("Invalid rgb image"); + if(gamma_precompensated_rgb_image.attributes.hasColorspace(SRGB)) throw ImageException("Invalid colorspace attribute"); + if(!gamma_precompensated_rgb_image.attributes.hasRange(0,1)) throw ImageException("Invalid range attribute"); + if(!((old_gamma>0.0)&&(old_gamma<1.0))) throw ImageException("Invalid gamma attribute"); + } + linear_rgb_image.setZero(gamma_precompensated_rgb_image.width(),gamma_precompensated_rgb_image.height()); + linear_rgb_image.attributes=gamma_precompensated_rgb_image.attributes; + + for(int channel=0; channel GAMMA +// when converting from linear to gamma precompensated the gamma parameter mus be in range (0.0,1.0) +template +inline void convert_linear_rgb_to_gamma_precompensated_rgb(const img::Image &linear_rgb_image, img::Image &gamma_precompensated_rgb_image, ScalarType2 gamma=ScalarType2(1.0/2.2)) +{ + assert(linear_rgb_image.isValid()); + assert(!linear_rgb_image.attributes.hasColorspace(SRGB)); + assert(linear_rgb_image.attributes.hasRange(0.0,1.0)); + assert(linear_rgb_image.attributes.hasGamma(1.0)); + assert((gamma>0.0)&&(gamma<1.0)); + if(Safe1 || Safe2){ + if(!linear_rgb_image.isValid()) throw img::ImageException("Invalid rgb image"); + if(linear_rgb_image.attributes.hasColorspace(SRGB)) throw ImageException("Invalid colorspace attribute"); + if(!linear_rgb_image.attributes.hasRange(0.0,1.0)) throw ImageException("Invalid range attribute"); + if(!linear_rgb_image.attributes.hasGamma(1.0)) throw ImageException("Invalid gamma attribute"); + if(!((gamma>0.0)&&(gamma<1.0))) throw ImageException("Invalid gamma parameter"); + } + gamma_precompensated_rgb_image.setZero(linear_rgb_image.width(),linear_rgb_image.height()); + gamma_precompensated_rgb_image.attributes=linear_rgb_image.attributes; + + for(int channel=0; channel LINEAR +template +inline void convert_gamma_precompensated_srgb_to_linear_srgb(const img::Image &gamma_precompensated_srgb_image, img::Image &linear_srgb_image) +{ + assert(gamma_precompensated_srgb_image.isValid()); + assert(gamma_precompensated_srgb_image.attributes.hasColorspace(SRGB)); + assert(gamma_precompensated_srgb_image.attributes.hasRange(0.0,1.0)); + assert(!gamma_precompensated_srgb_image.attributes.hasGamma(1.0)); + if(Safe1 || Safe2){ + if(!gamma_precompensated_srgb_image.isValid()) throw ImageException("Invalid rgb image"); + if(!gamma_precompensated_srgb_image.attributes.hasColorspace(SRGB)) throw ImageException("Invalid colorspace attribute"); + if(!gamma_precompensated_srgb_image.attributes.hasRange(0,1)) throw ImageException("Invalid range attribute"); + if(gamma_precompensated_srgb_image.attributes.hasGamma(1.0)) throw ImageException("Invalid gamma attribute"); + } + linear_srgb_image.setZero(gamma_precompensated_srgb_image.width(),gamma_precompensated_srgb_image.height()); + linear_srgb_image.attributes=gamma_precompensated_srgb_image.attributes; + + for(int channel=0; channel GAMMA +template +inline void convert_linear_srgb_to_gamma_precompensated_srgb(const img::Image &linear_srgb_image, img::Image &gamma_precompensated_srgb_image) +{ + assert(linear_srgb_image.isValid()); + assert(linear_srgb_image.attributes.hasColorspace(SRGB)); + assert(linear_srgb_image.attributes.hasRange(0.0,1.0)); + assert(linear_srgb_image.attributes.hasGamma(1.0)); + if(Safe1 || Safe2){ + if(!linear_srgb_image.isValid()) throw img::ImageException("Invalid rgb image"); + if(!linear_srgb_image.attributes.hasColorspace(SRGB)) throw ImageException("Invalid colorspace attribute"); + if(!linear_srgb_image.attributes.hasRange(0.0,1.0)) throw ImageException("Invalid range attribute"); + if(!linear_srgb_image.attributes.hasGamma(1.0)) throw ImageException("Invalid gamma attribute"); + } + gamma_precompensated_srgb_image.setZero(linear_srgb_image.width(),linear_srgb_image.height()); + gamma_precompensated_srgb_image.attributes=linear_srgb_image.attributes; + + for(int channel=0; channel XYZ +template +inline void convert_srgb_to_xyz(const img::Image<3,ScalarType1,Safe1> &rgb_image, img::Image<3,ScalarType2,Safe2> &xyz_image) +{ + assert( rgb_image.isValid()); + assert( rgb_image.attributes.hasRange(0.0,1.0)); + assert( rgb_image.attributes.hasGamma(1.0)); + assert( rgb_image.attributes.hasColorspace(img::RGB) ); + if(Safe1 || Safe2){ + if(!rgb_image.isValid()) throw img::ImageException("Invalid rgb image"); + if(!rgb_image.attributes.hasRange(0.0,1.0)) throw img::ImageException("Invalid range attribute"); + if(!rgb_image.attributes.hasGamma(1.0)) throw img::ImageException("Invalid gamma attribute"); + if(!rgb_image.attributes.hasColorspace(img::RGB)) throw img::ImageException("Invalid colorspace attribute"); + } + xyz_image.setZero(rgb_image.width(),rgb_image.height()); + xyz_image.attributes=rgb_image.attributes; + + for(int x_coord=0; x_coord(rgb_image.getValue(x_coord,y_coord,0)); + const ScalarType2 G = static_cast(rgb_image.getValue(x_coord,y_coord,1)); + const ScalarType2 B = static_cast(rgb_image.getValue(x_coord,y_coord,2)); + + xyz_image.setValue(x_coord,y_coord,0, ( 0.412453f*R + 0.357580f*G + 0.180423f*B ) ); + xyz_image.setValue(x_coord,y_coord,1, ( 0.212671f*R + 0.715160f*G + 0.072169f*B ) ); + xyz_image.setValue(x_coord,y_coord,2, ( 0.019334f*R + 0.119193f*G + 0.950227f*B ) ); + } + xyz_image.setColorspace(img::CIE_XYZ); +} + +// XYZ -> SRGB +template +inline void convert_xyz_to_rgb(const img::Image<3,ScalarType1,Safe1> &xyz_image, img::Image<3,ScalarType2,Safe2> &rgb_image) +{ + assert( xyz_image.isValid()); + assert( xyz_image.attributes.hasRange(0.0,1.0)); + assert( xyz_image.attributes.hasGamma(1.0)); + assert( xyz_image.attributes.hasColorspace(img::CIE_XYZ) ); + + if(Safe1 || Safe2){ + if(!xyz_image.isValid()) throw img::ImageException("Invalid xyz image"); + if(!xyz_image.attributes.hasRange(0.0,1.0)) throw img::ImageException("Invalid range attribute"); + if(!xyz_image.attributes.hasGamma(1.0)) throw img::ImageException("Invalid gamma attribute"); + if(!rgb_image.attributes.hasColorspace(img::CIE_XYZ)) throw img::ImageException("Invalid colorspace attribute"); + } + rgb_image.setZero(xyz_image.width(),xyz_image.height()); + rgb_image.attributes=xyz_image.attributes; + + for(int x=0; x(xyz_image.getValue(x,y,0)); + const ScalarType2 Y = static_cast(xyz_image.getValue(x,y,1)); + const ScalarType2 Z = static_cast(xyz_image.getValue(x,y,2)); + + const ScalarType2 R = 3.240479f*X + -1.537150f*Y + -0.498535f*Z; + const ScalarType2 G = -0.969256f*X + 1.875992f*Y + 0.041556f*Z; + const ScalarType2 B = 0.055648f*X + -0.204043f*Y + 1.057311f*Z; + + rgb_image.setValue(x,y,0, ( R<0.0f?0.0f:(R>1.0f?1.0f:R) ) ); + rgb_image.setValue(x,y,1, ( G<0.0f?0.0f:(G>1.0f?1.0f:G) ) ); + rgb_image.setValue(x,y,2, ( B<0.0f?0.0f:(B>1.0f?1.0f:B) ) ); + } + rgb_image.setColorspace(img::RGB); +} + +///ora nn ho tempo: +//// AltriRGB -> XYZ +//// XYZ -> AltriRGB +//// XYZ -> LAB +//// LAB -> XYZ +//template +//inline void convert_xyz_to_lab(const img::Image<3,ScalarType1,Safe1> &xyz_image, img::Image<3,ScalarType2,Safe2> &lab_image) +//{ +// assert(xyz_image.isValid()); +// if(Safe1 || Safe2){ +// if(!xyz_image.isValid()) throw img::ImageException("Invalid xyz image"); +// } +// lab_image.setZero(xyz_image.width(),xyz_image.height()); +// +// const ScalarType2 one_third=0.33333333333333333333333333333333333333333333333333333333333333f; +// const ScalarType2 Xn = 0.9513f; +// const ScalarType2 Yn = 1.000f; +// const ScalarType2 Zn = 1.0886f; +// +// for(int x=0; x(xyz_image.getValue(x,y,0))/Xn,one_third); +// const ScalarType2 Y = static_cast(xyz_image.getValue(x,y,1))/Yn; +// const ScalarType2 Y_third = pow(Y,one_third); +// const ScalarType2 Z_third = pow(static_cast(xyz_image.getValue(x,y,2))/Zn,one_third); +// +// lab_image.setValue(x,y,0, (Y > 0.008856f)?((116.0f*(Y_third)) - 16.0f):(903.3f*Y) ); +// lab_image.setValue(x,y,1, 500.0f * ((X_third) - (Y_third)) ); +// lab_image.setValue(x,y,2, 200.0f * ((Y_third) - (Z_third)) ); +// } +//} +// +//template +//inline void convert_lab_to_xyz(const img::Image<3,ScalarType1,Safe1> &lab_image, img::Image<3,ScalarType2,Safe2> &xyz_image) +//{ +// assert(0); // sistemare attributi +// assert(lab_image.isValid()); +// if(Safe1 || Safe2){ +// if(!lab_image.isValid()) throw img::ImageException("Invalid lab image"); +// } +// xyz_image.setZero(lab_image.width(),lab_image.height()); +// +// const ScalarType2 Xn = 0.9513f; +// const ScalarType2 Yn = 1.000f; +// const ScalarType2 Zn = 1.0886f; +// +// for(int x=0; x(lab_image.getValue(x,y,0))+16.0f)/116.0f; +// +// xyz_image.setValue(x,y,0, Xn * pow(P + (lab_image.getValue(x,y,1)/500.0f), 3.0f) ); +// xyz_image.setValue(x,y,1, Yn * pow(P, 3.0f) ); +// xyz_image.setValue(x,y,2, Zn * pow(P - (lab_image.getValue(x,y,2)/200.0f), 3.0f) ); +// } +//} + +} // end namespace img + +#endif /*IMG_CS_BASE_H_*/ diff --git a/img/img_filter.h b/img/img_filter.h new file mode 100755 index 00000000..88dba84f --- /dev/null +++ b/img/img_filter.h @@ -0,0 +1,388 @@ +#ifndef IMG_FILTER_H_ +#define IMG_FILTER_H_ + +// functions that operate on pixels +// in this file code readability is preferred over speed and memory optimizations + +#include +#include +#include + +namespace img { + +template +inline void normalize(Image &image) +{ + assert(image.isValid()); + assert(image.attributes.hasRange(0,255)); + if(Safe){ + if(!image.isValid()) throw ImageException("Invalid image"); + if(!image.attributes.hasRange(0,255)) throw ImageException("Invalid range attribute"); + } + ScalarType max=maxValue(image); + ScalarType min=minValue(image); + ScalarType scale=255.0f/(max-min); + + ScalarType* array = image.dataValues(); + int length =image.dataValuesSize(); + + for(int offset=0;offset +inline Image getNormalized(const Image &image) +{ + Image i(image); + normalize(i); + return i; +} + +template +inline void convolution(const Image &source,Image &destination,const DestScalarType *matrix,int matrix_width,int matrix_height) +{ + assert(source.isValid()); + assert(matrix != NULL); + assert((matrix_width > 0) && ((matrix_width)%2 == 1)); + assert((matrix_height > 0) && ((matrix_height)%2 == 1)); + if(SrcSafe || DestSafe){ + if(!source.isValid()) throw ImageException("Invalid image"); + if( matrix == NULL) throw ImageException("NULL convolution matrix"); + if( !((matrix_width > 0) && ((matrix_width)%2 == 1)) ) throw ImageException("Matrix width must be a positive odd number"); + if( !((matrix_height > 0) && ((matrix_height)%2 == 1)) ) throw ImageException("Matrix height must be a positive odd number"); + } + destination.setZero(source.width(),source.height()); + destination.attributes=source.attributes; + + int x_radius=(matrix_width-1)/2; + int y_radius=(matrix_height-1)/2; + + // per ogni canale + for(int channel=0; channel < Channels ;channel++){ // canali immagine + + // per ogni riga + for (int y = 0; y < source.height(); y++){ // righe immagine + + // per ogni pixel nella riga + for (int x = 0; x < source.width(); x++){ // colonne immagine + DestScalarType sum=0.0f; + int offset=0; + + for(int my = y-y_radius; my <= y+y_radius; my++) // righe matrice + for(int mx = x-x_radius; mx <= x+x_radius; mx++) //colonne matrice + sum += matrix[offset++] * static_cast(source.getValueAsClamped(mx,my,channel)); + + destination.setValue(x,y,channel,sum); + + } // colonne immagine + } // righe immagine + } // canali immagine +} + +template // get[filter]ed() functions constrain return type to be the same of the parameter +inline Image getConvolved(const Image &image,const ScalarType *matrix,int matrix_width,int matrix_height) +{ + Image i; + convolution(image,i,matrix,matrix_width,matrix_height); + return i; +} + +template +inline void boxFilter(const Image &source,Image &destination,int radius) +{ + assert(radius > 0); + if(SrcSafe || DestSafe){ + if(radius <= 0) throw ImageException("Nonpositive radius"); + } + int matrix_side=2*radius+1; + int matrix_size=matrix_side*matrix_side; + DestScalarType* matrix=new DestScalarType[matrix_size]; + DestScalarType val=1.0f/matrix_size; + for(int i=0; i // get[filter]ed() functions constrain return type to be the same of the parameter +inline Image getBoxFiltered(const Image &image,int radius) +{ + Image i; + boxFilter(image,i,radius); + return i; +} + +template +inline void _gaussian(const int &radius,const ScalarType &sigma,ScalarType * &matrix,int &matrix_side) +{ + matrix_side=2*radius+1; + int matrix_size=matrix_side*matrix_side; + matrix=new ScalarType[matrix_size]; + + // calcolo la matrice + int offset=0; + const ScalarType c = -0.5f / (sigma*sigma); + + for(int y = -radius;y <= radius; y++) + for(int x = -radius;x <= radius; x++) + matrix[offset++] = exp( (x*x+y*y)*c ); + + // porto la matrice a somma 1 + ScalarType sum=0.0f; + for(int i=0;i +inline void GaussianSmooth(const Image &source,Image &destination,const int radius) +{ + assert(radius > 0.0f); + if(SrcSafe || DestSafe){ + if(radius <= 0.0f) throw ImageException("Nonpositive radius"); + } + const DestScalarType sigma = radius/3.0f; + DestScalarType *matrix=NULL; + int matrix_side=0; + _gaussian(radius,sigma,matrix,matrix_side); + + convolution(source,destination,matrix,matrix_side,matrix_side); + delete [] matrix; +} + +template // get[filter]ed() functions constrain return type to be the same of the parameter +inline Image getGaussianSmoothed(const Image &image,const int radius) +{ + Image i; + GaussianSmooth(image,i,radius); + return i; +} + +template +inline void LaplacianFilter(const Image &source,Image &destination) +{ + DestScalarType *matrix=new DestScalarType[9]; + matrix[0]= 0.0f; matrix[1]= 1.0f; matrix[2]= 0.0f; + matrix[3]= 1.0f; matrix[4]=-4.0f; matrix[5]= 1.0f; + matrix[6]= 0.0f, matrix[7]= 1.0f; matrix[8]= 0.0f; + + convolution(source,destination,matrix,3,3); + delete [] matrix; +} + +template // get[filter]ed() functions constrain return type to be the same of the parameter +inline Image getLaplacianFiltered(const Image &image) +{ + Image i; + LaplacianFilter(image,i); + return i; +} + +template +inline void _laplacian_of_gaussian(const int &radius,const ScalarType &sigma,ScalarType * &matrix,int &matrix_side) +{ + matrix_side=2*radius+1; + int matrix_size=matrix_side*matrix_side; + matrix=new ScalarType[matrix_size]; + + // calcolo la matrice + int offset=0; + + const ScalarType c1 = -0.5f/(sigma*sigma); + ScalarType c; + + for(int y = -radius;y <= radius; y++) + for(int x = -radius;x <= radius; x++){ + c = (x*x+y*y) * c1; + matrix[offset++] = (1+c) * exp(c); + } + + // porto la matrice a somma 1 + ScalarType sum=0.0f; + for(int i=0;i +inline void LoGFilter(const Image &source,Image &destination,int radius) +{ + assert(radius > 0.0f); + if(SrcSafe || DestSafe){ + if(radius <= 0.0f) throw ImageException("Nonpositive radius"); + } + DestScalarType *matrix=NULL; + int matrix_side=0; + DestScalarType sigma = radius/3.0f; + _laplacian_of_gaussian(radius,sigma,matrix,matrix_side); + + convolution(source,destination,matrix,matrix_side,matrix_side); + delete [] matrix; +} + +template // get[filter]ed() functions constrain return type to be the same of the parameter +inline Image getLoGFiltered(const Image &image,int radius) +{ + Image i; + LoGFilter(image,i,radius); + return i; +} + +template +inline void DoGFilter(const Image &source,Image &destination,int radius1,int radius2) +{ + assert(radius1 > 0.0f); + assert(radius2 > 0.0f); + assert(radius2 > radius1); + if(SrcSafe || DestSafe){ + if(radius1 <= 0.0f) throw ImageException("Nonpositive radius1"); + if(radius2 <= 0.0f) throw ImageException("Nonpositive radius2"); + if(radius2 <= radius1) throw ImageException("radius2 is less than radius1"); + } + + int matrix_side=0; + + DestScalarType *m1=NULL,*m2=NULL; + const DestScalarType sigma1=radius1/3.0f; + const DestScalarType sigma2=radius2/3.0f; + + // ottengo la prima gaussiana (col radius della seconda) + _gaussian(radius2,sigma1,m1,matrix_side); + // ottengo la seconda gaussiana + _gaussian(radius2,sigma2,m2,matrix_side); + + int matrix_size=matrix_side*matrix_side; + + + // sottraggo alla prima gaussiana la seconda + for(int i=0;i // get[filter]ed() functions constrain return type to be the same of the parameter +inline Image getDoGFiltered(const Image &image,int radius1,int radius2) +{ + Image i; + DoGFilter(image,i,radius1,radius2); + return i; +} + +template +inline void UnsharpMask(const Image &source,Image &destination,int radius,float factor) +{ + assert(radius > 0); + assert(factor > 0.0f); + if(SrcSafe || DestSafe){ + if(radius <= 0.0f) throw ImageException("Nonpositive radius"); + if(factor <= 0.0f) throw ImageException("Nonpositive factor"); + } + // metto l'immagine smoothata in destination + GaussianSmooth(source,destination,radius); + + DestScalarType* source_array = source.dataValues(); + DestScalarType* destination_array = destination.dataValues(); + int length = source.dataValuesSize(); + + // unsharpo destination in loco + for(int offset=0;offset // get[filter]ed() functions constrain return type to be the same of the parameter +inline Image getUnsharpMasked(const Image &image,int radius,float factor) +{ + Image i; + UnsharpMask(image,i,radius,factor); + return i; +} + +template +inline void medianFilter(const Image &source,Image &destination,int radius) +{ + assert(source.isValid()); + assert(radius > 0); + if(SrcSafe || DestSafe){ + if(!source.isValid()) throw ImageException("Invalid image"); + if(radius <= 0) throw ImageException("Nonpositive radius"); + } + destination.setZero(source.width(),source.height()); + destination.attributes=source.attributes; + + // per ogni canale + for(int channel=0; channel < Channels;channel++){ // canali immagine + + // per ogni riga + for (int y = 0; y < source.height(); y++){ // righe immagine + + // per ogni pixel nella riga + for (int x = 0; x < source.width(); x++){ // colonne immagine + + // memorizzo i valori dell'intorno + std::vector v; + for(int my = y-radius; my <= y+radius; my++) // righe intorno + for(int mx = x-radius; mx <= x+radius; mx++) //colonne intorno + if (source.isInside(mx,my)) + v.push_back(static_cast(source.getValue(mx,my,channel))); + + // ottengo la mediana + int s=v.size(); + assert(s>0); + nth_element (v.begin(), v.begin()+(s/2), v.end()); + DestScalarType median = *(v.begin()+(s/2)); + if((s%2)==0) { // even s: mean of the 2 middle elements + nth_element (v.begin(), v.begin()+(s/2)+1, v.end()); + median = ( *(v.begin()+(s/2)+1) + median ) /2.0f; + } + // aggiorno il valore alla mediana dell'intorno + destination.setValue(x,y,channel,median); + + } // colonne immagine + } // righe immagine + } // canali immagine +} + +template // get[filter]ed() functions constrain return type to be the same of the parameter +inline Image getMedianFiltered(const Image &image,int radius) +{ + Image i; + medianFilter(image,i,radius); + return i; +} + +template +inline void channels_mean(const img::Image &channels_image, img::Image<1,ScalarType2,Safe2> &mean_image) +{ + assert(channels_image.isValid()); + if(Safe1 || Safe2){ + if(!channels_image.isValid()) throw img::ImageException("channels_image rgb image"); + } + mean_image.setZero(channels_image.width(),channels_image.height()); + mean_image.attributes=channels_image.attributes; + + for (int y_coord = 0; y_coord < channels_image.height(); ++y_coord) + for (int x_coord = 0; x_coord < channels_image.width(); ++x_coord){ + ScalarType2 sum = ScalarType2(0.0); + for (int channel=0; channel(channels_image.getValue(x_coord, y_coord, channel)); + mean_image.setValue(x_coord, y_coord, 0, sum / ScalarType2(Channels) ); + } +} + +} //end namespace img + +#endif /*IMG_FILTER_H_*/ diff --git a/img/img_image.h b/img/img_image.h new file mode 100755 index 00000000..9c098755 --- /dev/null +++ b/img/img_image.h @@ -0,0 +1,512 @@ +#ifndef IMG_IMAGE_H_ +#define IMG_IMAGE_H_ +/*! \file img_image.h + \brief definition of the generic image class + + This header contains the image class definition. +*/ + +#include "img/img_base.h" +#include "img/img_attributes.h" + +namespace img { + +/*! \brief the generic image class + + The image class is templated over three parameters: + - The number of the image channels: an image can have from one to an arbitrary large number of channels, i.e. values per pixel. The default is to have 3 color channels. + - The type of the values: the image stores the pixels' values in floating-point variables. The default is to have double floating point precision, the other option is to have single floating point precision. Static assertions are used to ensure that the value type of an image is a floating point number. + - The safeness: the image throws runtime exceptions when its member functions are called with "wrong" parameters. This behavior, active by default, can be disabled in order to speed up the computation. The parameter correctness is also independently checked with the dynamic assertions mechanism that can be disabled by compiling the code in "release" mode. + + The image data is packed by the pixel, to optimize multichannel processing efficiency. The image class interface is restricted to pixel and metadata access, processing and I/O are entirely delegated to external functions. The data is accessible in multiple ways: value and pixel wise, using float coordinates with nearest and bilinear interpolation, viewing the image with clamping, direct access to the data and so on. + + An auxiliary structured attribute contains all the metadata information, currently this data consists in the specification of the numeric range of the values, the image color space, the white point and the gamma compression of the image. The various functions checks these data before processing, failing if the image is not compatible with the operation in course. +*/ +template +class Image //TODO: Safe=false prima di mettere in vcg +{ +private: + // Data is private but controlled data wiews are + // accessible from accessors + + /// width of the image + int _width; + /// height of the image + int _height; + /// data buffer + ScalarType *_data; + +// constructors +public: + /// the auxiliary structured attribute that contains all the metadata information. + ImgAttributes attributes; + + + /*! \brief default image constructor + + Creates a 0 x 0 pixel image with no data and default attributes + */ + Image() // creates a 0 x 0 image + :_width(0),_height(0),_data(NULL),attributes() + { + STATIC_ASSERT( Channels>0 ); + STATIC_FLOAT_OR_DOUBLE_TYPECHECK( ScalarType ); + } + + /*! \brief (deep) copy constructor when all template parameters matches + + An explicit copy constructor is needed because when all template parameters matches the templated copy constructor is not consideredand and a wrong copy constructor is synthetized by compiler. + + \param image the image to be copied + */ + Image(const Image &image) // copy constructor (deep copy) + { + STATIC_ASSERT( Channels>0 ); + STATIC_FLOAT_OR_DOUBLE_TYPECHECK( ScalarType ); + + assert(image._width > 0); + assert(image._height > 0); + assert(image._data != NULL); + if(Safe) { + if(image._width <= 0) throw ImageException("Image(Image): Nonpositive width"); + if(image._height <= 0) throw ImageException("Image(Image): Nonpositive height"); + if(image._data == NULL) throw ImageException("Image(Image): NULL data"); + } + _width = image._width; + _height = image._height; + _data = new ScalarType[Channels * _width * _height]; + attributes=image.attributes; + + memcpy(_data, image._data, sizeof(ScalarType) * Channels * _width * _height); + } + + /*! \brief (deep) copy constructor when some template parameters differs + + \param image the image to be copied + */ + template + Image(const Image &image) // templated copy constructor (deep copy) + :_width(0),_height(0),_data(NULL) + { + STATIC_ASSERT( Channels>0 ); + STATIC_FLOAT_OR_DOUBLE_TYPECHECK( ScalarType ); + + assert(image._width > 0); + assert(image._height > 0); + assert(image._data != NULL); + if(Safe || OtherSafe) { + if(image._width <= 0) throw ImageException("Image(Image): Nonpositive width"); + if(image._height <= 0) throw ImageException("Image(Image): Nonpositive height"); + if(image._data == NULL) throw ImageException("Image(Image): NULL data"); + } + _width = image._width; + _height = image._height; + _data = new ScalarType[Channels * _width * _height]; + attributes=image.attributes; + + if(typeid( ScalarType ) == typeid( OtherScalarType )) + memcpy(_data, image._data, sizeof(ScalarType) * Channels * _width * _height); + else + for(int offset=0;offset< Channels * _width * _height; ++offset) + _data[offset] = static_cast(image._data[offset]); + } + + /*! \brief blank image constructor + + Creates an arg_width x arg_height pixel image with 0-valued data and default attributes + \param arg_width the width of the blank image + \param arg_height the height of the blank image + */ + Image(int arg_width,int arg_height) + :_width(arg_width),_height(arg_height),_data(NULL),attributes() + { + STATIC_ASSERT( Channels>0 ); + STATIC_FLOAT_OR_DOUBLE_TYPECHECK( ScalarType ); + + assert(arg_width>0); + assert(arg_height>0); + if(Safe) { + if(arg_width <= 0) throw ImageException("Image(int,int): Nonpositive width"); + if(arg_height <= 0) throw ImageException("Image(int,int): Nonpositive height"); + } + _data = new ScalarType[Channels * _width * _height]; + for(int offset=0;offset< Channels * _width * _height; ++offset) + _data[offset] = 0.0f; + } + + /// the destructor + ~Image() + { + attributes.reset(); + if(_data!=NULL){ + delete [] _data; + _data=NULL; + } + } + +// public functions +public: + /*! \brief assignment operator (deep copy) + + \param image the image to be assigned to this instance + \return a pointer to this instance + */ + template + inline Image< Channels,ScalarType,Safe> & operator =(const Image &image) + { + assert(image._width > 0); + assert(image._height > 0); + assert(image._data != NULL); + if(Safe || OtherSafe) { + if(image._width <= 0) throw ImageException("operator =: Nonpositive width"); + if(image._height <= 0) throw ImageException("operator =: Nonpositive height"); + if(image._data == NULL) throw ImageException("operator =: NULL data"); + } + _width = image._width; + _height = image._height; + _data = new ScalarType[Channels * _width * _height]; + attributes=image.attributes; + + if(typeid( ScalarType ) == typeid( OtherSafe )) + memcpy(_data, image._data, sizeof(ScalarType) * Channels * _width * _height); + else + for(int offset=0;offset < Channels * _width * _height; ++offset) + _data[offset] = static_cast(image._data[offset]); + return *this; + } + + /*! \brief blanks and change the image dimensions + + Delete the current image data and create an arg_width x arg_height pixel image with 0-valued data and default attributes + \param arg_width the width of the blank image + \param arg_height the height of the blank image + */ + inline void setZero(int arg_width, int arg_height) + { + assert(arg_width>0); + assert(arg_height>0); + if(Safe) { + if(arg_width <= 0) throw ImageException("setZero: Nonpositive width"); + if(arg_height <= 0) throw ImageException("setZero: Nonpositive height"); + } + if(_data!=NULL){ + delete [] _data; + _data=NULL; + } + _width = arg_width; + _height = arg_height; + _data = new ScalarType[Channels * _width * _height]; + attributes.reset(); + + for(int offset=0;offset< Channels * _width * _height; ++offset) + _data[offset] = 0.0f; + } + + /*! \brief delete the image data + + Delete the current image data and create an 0 x 0 pixel image with no data. + */ + inline void deleteData() + { + if(_data!=NULL){ + delete [] _data; + _data=NULL; + } + _width = 0; + _height = 0; + } + + + /*! \brief get all the values of a pixel + + \param x the horizontal coordinate of the pixel + \param y the vertical coordinate of the pixel + \param ret_pixel return parameter that is filled with the pixel values + */ + inline void getPixel(int x, int y, ScalarType (& ret_pixel)[Channels]) const + { + assert( _data != NULL ); + assert( x >= 0 && x < _width ); + assert( y >= 0 && y < _height ); + if( Safe ){ + if ( _data == NULL ) throw ImageException("getPixel: NULL data"); + if ( !( x >= 0 && x < _width ) ) throw ImageException("getPixel: x out of bounds"); + if ( !( y >= 0 && y < _height ) ) throw ImageException("getPixel: y out of bounds"); + } + for (int channel=0;channel= 0 && x < _width ); + assert( y >= 0 && y < _height ); + if( Safe ){ + if ( _data == NULL ) throw ImageException("setPixel: NULL data"); + if ( !( x >= 0 && x < _width ) ) throw ImageException("setPixel: x out of bounds"); + if ( !( y >= 0 && y < _height ) ) throw ImageException("setPixel: y out of bounds"); + } + for (int channel=0;channel= 0 && x < _width ); + assert( y >= 0 && y < _height ); + assert( channel >=0 && channel < Channels ); + if( Safe ){ + if ( _data == NULL ) throw ImageException("getFloat: NULL data"); + if ( !( x >= 0 && x < _width ) ) throw ImageException("getFloat: x out of bounds"); + if ( !( y >= 0 && y < _height ) ) throw ImageException("getFloat: y out of bounds"); + if ( !( channel >=0 && channel < Channels ) ) throw ImageException("channel out of bounds"); + } + return _data[ (x + y * _width) * Channels + channel ]; + } + + /*! \brief set a single value of a pixel + + \param x the horizontal coordinate of the pixel + \param y the vertical coordinate of the pixel + \param channel the channel index + \param value the value that is assigned to the channel at the pixel + */ + inline void setValue(int x, int y, int channel, ScalarType value) + { + assert( _data != NULL ); + assert( x >= 0 && x < _width ); + assert( y >= 0 && y < _height ); + assert( channel >=0 && channel < Channels ); + if( Safe ){ + if ( _data == NULL ) throw ImageException("setFloat: NULL data"); + if ( !( x >= 0 && x < _width ) ) throw ImageException("setFloat: x out of bounds"); + if ( !( y >= 0 && y < _height ) ) throw ImageException("setFloat: y out of bounds"); + if ( !( channel >=0 && channel < Channels ) ) throw ImageException("channel out of bounds"); + } + _data[(x + y * _width) * Channels + channel] = value; + } + + /*! \brief get all the values of a pixel, clamping if the coordinates are out of bounds + + \param x the horizontal coordinate of the pixel + \param y the vertical coordinate of the pixel + \param ret_pixel return parameter that is filled with the pixel values + */ + inline void getPixelAsClamped(int x, int y, ScalarType (& ret_pixel)[Channels]) const + { + getPixel(x<0?0:(x<_width?x:_width-1), y<0?0:(y<_height?y:_height-1), ret_pixel); + } + + /*! \brief get a single value of a pixel, clamping if the coordinates are out of bounds + + \param x the horizontal coordinate of the pixel + \param y the vertical coordinate of the pixel + \param channel the channel index + \return the value of the channel at the pixel + */ + inline float getValueAsClamped(int x, int y, int channel) const + { + return getValue(x<0?0:(x<_width?x:_width-1), y<0?0:(y<_height?y:_height-1),channel); + } + + /*! \brief get all the values of a pixel, rounding the floating coordinates to the nearest pixel + + \param x the horizontal coordinate of the pixel + \param y the vertical coordinate of the pixel + \param ret_pixel return parameter that is filled with the pixel values + */ + inline void nearestPixel(float x, float y, ScalarType (& ret_pixel)[Channels]) const + { + getPixel(static_cast(floor(x+0.5f)),static_cast(floor(y+0.5f)), ret_pixel); + } + +///// chi gli serve se la implementa, ora non c'ho tempo +// inline void bilinearPixel(float x, float y, const ScalarType[] &pixel) const +// { +// assert( _data != NULL ); +// assert( x >= 0.0f && x <= _width-1.0f ); +// assert( y >= 0.0f && y <= _height-1.0f ); +// if( SAFE ){ +// if ( _data == NULL ) throw ImageException("bilinearPixel: NULL data"); +// if ( !( x >= 0.0f && x <= _width-1.0f ) ) throw ImageException("bilinearPixel: x out of bounds"); +// if ( !( y >= 0.0f && y <= _height-1.0f ) ) throw ImageException("bilinearPixel: y out of bounds"); +// } +// +// int x1 = static_cast(floor(x)); +// int y1 = static_cast(floor(y)); +// float a = 1.0f; +// float b = 1.0f; +// +// if( x1 == _width ) +// x1--; +// else +// a = x - x1; +// if( y1 == _height ) +// y1--; +// else +// b = y - y1; +// +// float a1 = (1.0f - a); +// float b1 = (1.0f - b); +// +// int i = x1 + y1 * _width; +// +// return _data[i] * a1 * b1 +// + _data[i + 1] * a * b1 +// + _data[i + _width] * a1 * b + +// _data[i + _width + 1] * a * b; +// } + +// accessors +public: + /*! \brief get the width of the image + + \return the width of the image + */ + inline int width() const + { + return _width; + } + + /*! \brief get the height of the image + + \return the height of the image + */ + inline int height() const + { + return _height; + } + + /*! \brief set the width of the image + + \warning changing the image size without changing the data buffer accordingly can lead to memory errors + \param width the new width of the image + */ + inline void setWidth(int width) + { + assert(width > 0); + if(Safe){ + if(width <= 0) throw ImageException("setWidth: Nonpositive width"); + } + _width=width; + } + + /*! \brief set the height of the image + + \warning changing the image size without changing the data buffer accordingly can lead to memory errors + \param height the new height of the image + */ + inline void setHeight(int height) + { + assert(height > 0); + if(Safe){ + if(height <= 0) throw ImageException("setHeight: Nonpositive height"); + } + _height=height; + } + + /*! \brief get a const pointer to the image databuffer + + \warning this function exposes the internal structure of the image, mind your accesses. + \return a const pointer to the image databuffer + */ + inline ScalarType* dataValues() const + { + return _data; + } + + /*! \brief get the size of the image databuffer + + \return the size of the image databuffer + */ + inline int dataValuesSize() const + { + return _width * _height * Channels; + } + + + /*! \brief set the image databuffer + + \warning this function modifies the internal structure of the image! + \param data a pointer to the new image databuffer + */ + inline void setValues(ScalarType* data) + { + assert(data!=NULL); + if(Safe){ + if(data == NULL) throw ImageException("setValues: NULL data"); + } + _data = data; + } + + /*! \brief checks if the given coordinates are inside the image bounds + + \param x the horizontal coordinate + \param y the vertical coordinate + \return true if the given coordinates are inside the image bounds, false otherwise + */ + inline bool isInside(int x, int y) const + { + return x >= 0 && x < _width && y >= 0 && y < _height; + } + + /*! \brief checks if the given float coordinates are inside the image bounds + + \param x the horizontal coordinate + \param y the vertical coordinate + \return true if the given coordinates are inside the image bounds, false otherwise + */ + inline bool isInside(float x, float y) const + { + return x >= 0.0f && x <= _width-1.0f && y >= 0.0f && y <= _height-1.0f; + } + + /*! \brief checks if the image has been initialized + + \return true if the data is not null and the minimun side is longer than 0 pixel, false otherwise + */ + inline bool isValid() const + { + return _data != NULL && _width > 0 && _height > 0; + } + + /*! \brief get the number of channels of the image + + \return the number of channels of the image + */ + inline int channels() const + { + return Channels; + } + + /*! \brief set a value in the image databuffer + + \warning this function modifies the internal structure of the image! + \param i the index of the databuffer + \param value the value to set in the i position of the databuffer + */ + inline void setRawValue(int i, ScalarType value) + { + _data[i] = value; + } + +}; + +} //end namespace img + +#endif /*IMG_IMAGE_H_*/ diff --git a/img/img_info.h b/img/img_info.h new file mode 100755 index 00000000..970b6387 --- /dev/null +++ b/img/img_info.h @@ -0,0 +1,45 @@ +#ifndef IMG_INFO_H_ +#define IMG_INFO_H_ + +// functions that extrapolate information from read-only images + +namespace img { + +template +inline ScalarType minValue(const Image &image) +{ + assert(image.isValid()); + if(Safe){ + if(!image.isValid()) throw ImageException("Invalid image"); + } + ScalarType* array = image.dataValues(); + int length =image.dataValuesSize(); + + ScalarType min = array[0]; + for(int offset=0;offset array[offset]) + min = array[offset]; + return min; +} + +template +inline ScalarType maxValue(const Image &image) +{ + assert(image.isValid()); + if(Safe){ + if(!image.isValid()) throw ImageException("Invalid image"); + } + ScalarType* array = image.dataValues(); + int length =image.dataValuesSize(); + + ScalarType max = array[0]; + for(int offset=0;offset +#include +#include +namespace img { + +template +inline bool saveNativeFormat(const Image &image, const char *filename) +{ + assert(image.isValid()); + if(Safe){ + if(!image.isValid()) throw ImageException("Invalid image"); + } + using namespace std; + ofstream output (filename, ios::out|ios::binary); + + if (output.is_open()) { + int channels=Channels; + output.write(reinterpret_cast(& channels), sizeof(int)); + int scalartype=0; + if(typeid(ScalarType) == typeid(float)) { + scalartype=1; + } else if(typeid(ScalarType) == typeid(double)) { + scalartype=2; + } else { + assert(0); + } + output.write(reinterpret_cast(& scalartype), sizeof(int)); + int width=image.width(); + output.write(reinterpret_cast(& width), sizeof(int)); + int height=image.height(); + output.write(reinterpret_cast(& height), sizeof(int)); + output.write(reinterpret_cast(& image.attributes), sizeof(ImgAttributes)); + output.write(reinterpret_cast(image.dataValues()), sizeof(ScalarType) * image.dataValuesSize()); + output.flush(); + output.close(); + return true; + } + if(Safe) + throw ImageException("Unable to save file"); + return false; +} + +template +inline bool openNativeFormat(const char *filename, Image &image) +{ + + using namespace std; + ifstream input (filename, ios::in|ios::binary); + if (input.is_open()) { + int channels; + input.read(reinterpret_cast(&channels), sizeof(int)); + assert(channels==Channels); + int scalartype; + input.read(reinterpret_cast(&scalartype), sizeof(int)); + if(typeid(ScalarType) == typeid(float)) { + assert(scalartype==1); + } else if(typeid(ScalarType) == typeid(double)) { + assert(scalartype==2); + } else { + assert(0); + } + int width; + input.read(reinterpret_cast(&width), sizeof(int)); + int height; + input.read(reinterpret_cast(&height), sizeof(int)); + image.setZero(width,height); + input.read(reinterpret_cast(& image.attributes), sizeof(ImgAttributes)); + input.read(reinterpret_cast(image.dataValues()), sizeof(ScalarType) * image.dataValuesSize()); + input.close(); + return true; + } + if(Safe) + throw ImageException("Unable to open file"); + return false; +} + +} //end namespace img + +#endif /*IMG_IO_H_*/ diff --git a/img/img_scalar.h b/img/img_scalar.h new file mode 100755 index 00000000..c7bbeee9 --- /dev/null +++ b/img/img_scalar.h @@ -0,0 +1,108 @@ +#ifndef IMG_SCALAR_H_ +#define IMG_SCALAR_H_ +/*! \file img_scalar.h + \brief functions that operate on the pixel values + + This header contains some utility functions for the scalar values of the image pixels. +*/ + +#include "img/img_base.h" + +namespace img { + +/*! \brief clamp a scalar value + + \param value the value that is to be clamped + \param minval the minimum possible value after clamping + \param maxval the the maximum possible value after clamping + \return the clamped value +*/ +template +inline ScalarType clampValue(ScalarType value, ScalarType minval=ScalarType(0.0), ScalarType maxval=ScalarType(255.0)) +{ + STATIC_FLOAT_OR_DOUBLE_TYPECHECK(ScalarType); + if(value < minval) return minval; + if(value > maxval) return maxval; + return value; +} + +/*! \brief convert a scalar value to the nearest integer + + \param value the value that is to be rounded + \return the rounded value +*/ +template +inline int valueAsInt(ScalarType value) +{ + STATIC_FLOAT_OR_DOUBLE_TYPECHECK(ScalarType); + return static_cast(floor(static_cast(value)+0.5f)); +} + +/*! \brief take the maximum between three scalar values + + \param a one of the three values + \param b one of the three values + \param c one of the three values + \return the maximum value between a, b and c +*/ +template +inline ScalarType max3(ScalarType a, ScalarType b, ScalarType c) +{ + STATIC_FLOAT_OR_DOUBLE_TYPECHECK(ScalarType); + if ( b<=a && c<=a ) return a; + if ( a<=b && c<=b ) return b; + return c; +} + +/*! \brief take the minimum between three scalar values + + \param a one of the three values + \param b one of the three values + \param c one of the three values + \return the minimum value between a, b and c +*/ +template +inline ScalarType min3(ScalarType a, ScalarType b, ScalarType c) +{ + STATIC_FLOAT_OR_DOUBLE_TYPECHECK(ScalarType); + if ( a<=b && a<=c ) return a; + if ( b<=a && b<=c ) return b; + return c; +} + +/*! \brief take the median between three scalar values + + \param a one of the three values + \param b one of the three values + \param c one of the three values + \return the median value between a, b and c +*/ +template +inline ScalarType median3(ScalarType a, ScalarType b, ScalarType c) +{ + STATIC_FLOAT_OR_DOUBLE_TYPECHECK(ScalarType); + if ( (b<=a && a<=c) || (c<=a && a<=b) ) return a; + if ( (a<=b && b<=c) || (c<=b && b<=a) ) return b; + return c; +} + +/*! \brief compares two scalar values for (near) equality + + \param a one of the two values + \param b one of the two values + \param EPSILON the tolerance for considering a and b equal + \return true if a differs from b for less than EPSILON, false otherwhise +*/ +template +inline bool almostEqual(ScalarType a, ScalarType b, ScalarType EPSILON=ScalarType(10e-5)) +{ + STATIC_FLOAT_OR_DOUBLE_TYPECHECK(ScalarType); + ScalarType diff=a-b; + if (diff<0) + diff *= ScalarType(-1); + return diff < EPSILON; +} + +} //end namespace img + +#endif /*IMG_SCALAR_H_*/