added img module

This commit is contained in:
Paolo Cignoni 2009-07-14 14:43:19 +00:00
parent 08c2cae985
commit 1b9bcde35a
10 changed files with 1887 additions and 0 deletions

18
img/img.h Executable file
View File

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

246
img/img_attributes.h Normal file
View File

@ -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 <typename ScalarType=double>
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<ScalarType> &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<typename OtherScalarType>
ImgAttributes(const ImgAttributes<OtherScalarType> &attributes)
{
STATIC_FLOAT_OR_DOUBLE_TYPECHECK( ScalarType );
_colorspace = attributes._colorspace;
_gamma = static_cast<ScalarType>(attributes._gamma);
_range_min = static_cast<ScalarType>(attributes._range_min);
_range_max = static_cast<ScalarType>(attributes._range_max);
_reference_white_x = static_cast<ScalarType>(attributes._reference_white_x);
_reference_white_y = static_cast<ScalarType>(attributes._reference_white_y);
_reference_white_z = static_cast<ScalarType>(attributes._reference_white_z);
}
template<typename OtherScalarType>
inline ImgAttributes< ScalarType> & operator =(const ImgAttributes<OtherScalarType> &attributes)
{
_colorspace = attributes._colorspace;
_gamma = static_cast<ScalarType>(attributes._gamma);
_range_min = static_cast<ScalarType>(attributes._range_min);
_range_max = static_cast<ScalarType>(attributes._range_max);
_reference_white_x = static_cast<ScalarType>(attributes._reference_white_x);
_reference_white_y = static_cast<ScalarType>(attributes._reference_white_y);
_reference_white_z = static_cast<ScalarType>(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<typename OtherScalarType>
void getGamma(OtherScalarType &ret_gamma) const
{
STATIC_FLOAT_OR_DOUBLE_TYPECHECK( OtherScalarType );
ret_gamma = static_cast<OtherScalarType>(_gamma);
}
template<typename OtherScalarType>
void getRange(OtherScalarType &ret_range_min, OtherScalarType &ret_range_max) const
{
STATIC_FLOAT_OR_DOUBLE_TYPECHECK( OtherScalarType );
ret_range_min = static_cast<OtherScalarType>(_range_min);
ret_range_max = static_cast<OtherScalarType>(_range_max);
}
template<typename OtherScalarType>
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<OtherScalarType>(_reference_white_x);
ret_reference_white_y = static_cast<OtherScalarType>(_reference_white_y);
ret_reference_white_z = static_cast<OtherScalarType>(_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<ScalarType> &attributes)
{
_colorspace = attributes._colorspace;
}
void setGamma(const ImgAttributes<ScalarType> &attributes)
{
_gamma = attributes._gamma;
}
void setRange(const ImgAttributes<ScalarType> &attributes)
{
_range_min = attributes._range_min;
_range_max = attributes._range_max;
}
void setReferenceWhite(const ImgAttributes<ScalarType> &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<ScalarType> &attributes) const
{
return _colorspace == attributes._colorspace;
}
bool hasGamma(const ImgAttributes<ScalarType> &attributes) const
{
return _gamma == attributes._gamma;
}
bool hasRange(const ImgAttributes<ScalarType> &attributes) const
{
return (_range_min == attributes._range_min) &&
(_range_max == attributes._range_max);
}
bool hasReferenceWhite(const ImgAttributes<ScalarType> &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<ScalarType> &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_*/

69
img/img_base.h Executable file
View File

@ -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<bool> struct NON_TRUE_EXPR_CompileTimeError;
/// partial instantiation for the static assertion mechanism
template<> struct NON_TRUE_EXPR_CompileTimeError<true> {};
/// the static assertion mechanism
#define STATIC_ASSERT(exp) (NON_TRUE_EXPR_CompileTimeError< (exp) >())
/// base of the static typecheck mechanism
template<typename> struct NON_FLOAT_OR_DOUBLE_TYPE_CompileTimeError;
/// partial instantiation for the static typecheck mechanism
template<> struct NON_FLOAT_OR_DOUBLE_TYPE_CompileTimeError<float> {};
/// partial instantiation for the static typecheck mechanism
template<> struct NON_FLOAT_OR_DOUBLE_TYPE_CompileTimeError<double> {};
/// 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 <assert.h>
#include <math.h>
#include <exception>
#include <typeinfo>
/*! \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_*/

169
img/img_convert.h Executable file
View File

@ -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<typename SrcScalarType, bool SrcSafe,typename DestScalarType, bool DestSafe>
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<DestScalarType>(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<typename SrcScalarType, bool SrcSafe,typename DestScalarType, bool DestSafe>
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<DestScalarType>(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<typename SrcScalarType, bool SrcSafe,typename DestScalarType, bool DestSafe>
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<DestScalarType>(source.getValue(x_coord, y_coord, 0));
DestScalarType G = static_cast<DestScalarType>(source.getValue(x_coord, y_coord, 1));
DestScalarType B = static_cast<DestScalarType>(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<typename SrcScalarType, bool SrcSafe,typename DestScalarType, bool DestSafe>
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<DestScalarType>(source.getValue(x_coord, y_coord, 0)));
destination.setValue(x_coord, y_coord, 1, static_cast<DestScalarType>(source.getValue(x_coord, y_coord, 1)));
destination.setValue(x_coord, y_coord, 2, static_cast<DestScalarType>(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<typename SrcScalarType, bool SrcSafe,typename DestScalarType, bool DestSafe>
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<DestScalarType>(source.getValue(x_coord, y_coord, 0));
DestScalarType G = static_cast<DestScalarType>(source.getValue(x_coord, y_coord, 1));
DestScalarType B = static_cast<DestScalarType>(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<typename SrcScalarType, bool SrcSafe,typename DestScalarType, bool DestSafe>
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<DestScalarType>(source.getValue(x_coord, y_coord, 0)));
destination.setValue(x_coord, y_coord, 1, static_cast<DestScalarType>(source.getValue(x_coord, y_coord, 1)));
destination.setValue(x_coord, y_coord, 2, static_cast<DestScalarType>(source.getValue(x_coord, y_coord, 2)));
}
destination.attributes.setRange(source.attributes);
destination.attributes.setGamma(source.attributes);
}
// range conversion
template<int Channels, typename SrcScalarType, bool SrcSafe, typename DestScalarType, bool DestSafe>
inline void convert_range_0_255_to_0_1(const Image<Channels,SrcScalarType,SrcSafe> &source, Image<Channels,DestScalarType,DestSafe> &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.dataValuesSize();offset++)
destination.dataValues()[offset] = static_cast<DestScalarType>(source.dataValues()[offset]) / DestScalarType(255.0);
destination.attributes=source.attributes;
destination.attributes.setRange(0,1);
}
template<int Channels, typename SrcScalarType, bool SrcSafe, typename DestScalarType, bool DestSafe>
inline void convert_range_0_1_to_0_255(const Image<Channels,SrcScalarType,SrcSafe> &source, Image<Channels,DestScalarType,DestSafe> &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.dataValuesSize();offset++)
destination.dataValues()[offset] = static_cast<DestScalarType>(source.dataValues()[offset]) * DestScalarType(255.0);
destination.attributes=source.attributes;
destination.attributes.setRange(0,255);
}
} //end namespace img
#endif /*IMG_CONVERT_H_*/

248
img/img_cs_base.h Normal file
View File

@ -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<int Channels, typename ScalarType1, bool Safe1,typename ScalarType2, bool Safe2>
inline void convert_gamma_precompensated_rgb_to_linear_rgb(const img::Image<Channels,ScalarType1,Safe1> &gamma_precompensated_rgb_image, img::Image<Channels,ScalarType2,Safe2> &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<Channels; ++channel)
for(int x_coord=0; x_coord<gamma_precompensated_rgb_image.width(); ++x_coord)
for(int y_coord=0; y_coord<gamma_precompensated_rgb_image.height(); ++y_coord){
linear_rgb_image.setValue(x_coord,y_coord,channel, pow(ScalarType2(gamma_precompensated_rgb_image.getValue(x_coord,y_coord,channel)),ScalarType2(1.0)/old_gamma));
}
linear_rgb_image.attributes.setGamma(ScalarType2(1.0));
}
// RGB: LINEAR -> GAMMA
// when converting from linear to gamma precompensated the gamma parameter mus be in range (0.0,1.0)
template<int Channels, typename ScalarType1, bool Safe1,typename ScalarType2, bool Safe2>
inline void convert_linear_rgb_to_gamma_precompensated_rgb(const img::Image<Channels,ScalarType1,Safe1> &linear_rgb_image, img::Image<Channels,ScalarType2,Safe2> &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<Channels; ++channel)
for(int x_coord=0; x_coord<linear_rgb_image.width(); ++x_coord)
for(int y_coord=0; y_coord<linear_rgb_image.height(); ++y_coord){
gamma_precompensated_rgb_image.setValue(x_coord,y_coord,channel, pow(ScalarType2(linear_rgb_image.getValue(x_coord,y_coord,channel)),gamma));
}
gamma_precompensated_rgb_image.attributes.setGamma(gamma);
}
// SRGB: GAMMA -> LINEAR
template<int Channels, typename ScalarType1, bool Safe1,typename ScalarType2, bool Safe2>
inline void convert_gamma_precompensated_srgb_to_linear_srgb(const img::Image<Channels,ScalarType1,Safe1> &gamma_precompensated_srgb_image, img::Image<Channels,ScalarType2,Safe2> &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<Channels; ++channel)
for(int x_coord=0; x_coord<gamma_precompensated_srgb_image.width(); ++x_coord)
for(int y_coord=0; y_coord<gamma_precompensated_srgb_image.height(); ++y_coord){
ScalarType2 value = ScalarType2(gamma_precompensated_srgb_image.getValue(x_coord,y_coord,channel));
if(value<=ScalarType2(0.04045))
value = value / ScalarType2(12.92);
else
value = pow( (value+ScalarType2(0.055)) / ScalarType2(1.055), ScalarType2(2.4) );
linear_srgb_image.setValue(x_coord, y_coord, channel, value);
}
linear_srgb_image.attributes.setGamma(ScalarType2(1.0));
}
// SRGB: LINEAR -> GAMMA
template<int Channels, typename ScalarType1, bool Safe1,typename ScalarType2, bool Safe2>
inline void convert_linear_srgb_to_gamma_precompensated_srgb(const img::Image<Channels,ScalarType1,Safe1> &linear_srgb_image, img::Image<Channels,ScalarType2,Safe2> &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<Channels; ++channel)
for(int x_coord=0; x_coord<linear_srgb_image.width(); ++x_coord)
for(int y_coord=0; y_coord<linear_srgb_image.height(); ++y_coord){
ScalarType2 value = ScalarType2(linear_srgb_image.getValue(x_coord,y_coord,channel));
if(value<=ScalarType2(0.0031308))
value = value * ScalarType2(12.92);
else
value = (ScalarType2(1.055) * pow(value, ScalarType2(1.0/2.4))) - ScalarType2(0.055);
gamma_precompensated_srgb_image.setValue(x_coord, y_coord, channel, value);
}
gamma_precompensated_srgb_image.attributes.setGamma(ScalarType2(2.2)); // approssimation
}
// SRGB -> XYZ
template<typename ScalarType1, bool Safe1,typename ScalarType2, bool Safe2>
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.width(); ++x_coord)
for(int y_coord=0; y_coord<rgb_image.height(); ++y_coord){
const ScalarType2 R = static_cast<ScalarType2>(rgb_image.getValue(x_coord,y_coord,0));
const ScalarType2 G = static_cast<ScalarType2>(rgb_image.getValue(x_coord,y_coord,1));
const ScalarType2 B = static_cast<ScalarType2>(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<typename ScalarType1, bool Safe1,typename ScalarType2, bool Safe2>
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.width(); ++x)
for(int y=0; y<xyz_image.height(); ++y){
const ScalarType2 X = static_cast<ScalarType2>(xyz_image.getValue(x,y,0));
const ScalarType2 Y = static_cast<ScalarType2>(xyz_image.getValue(x,y,1));
const ScalarType2 Z = static_cast<ScalarType2>(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<typename ScalarType1, bool Safe1,typename ScalarType2, bool Safe2>
//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.width(); ++x)
// for(int y=0; y<xyz_image.height(); ++y){
// const ScalarType2 X_third = pow(static_cast<ScalarType2>(xyz_image.getValue(x,y,0))/Xn,one_third);
// const ScalarType2 Y = static_cast<ScalarType2>(xyz_image.getValue(x,y,1))/Yn;
// const ScalarType2 Y_third = pow(Y,one_third);
// const ScalarType2 Z_third = pow(static_cast<ScalarType2>(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<typename ScalarType1, bool Safe1,typename ScalarType2, bool Safe2>
//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.width(); ++x)
// for(int y=0; y<lab_image.height(); ++y){
// const ScalarType2 P = (static_cast<ScalarType2>(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_*/

388
img/img_filter.h Executable file
View File

@ -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 <math.h>
#include <vector>
#include <algorithm>
namespace img {
template<int Channels, typename ScalarType, bool Safe>
inline void normalize(Image<Channels,ScalarType,Safe> &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<length;offset++)
array[offset] = (array[offset]-min)*scale;
}
template<int Channels, typename ScalarType, bool Safe>
inline Image<Channels,ScalarType,Safe> getNormalized(const Image<Channels,ScalarType,Safe> &image)
{
Image<Channels,ScalarType,Safe> i(image);
normalize(i);
return i;
}
template<int Channels, typename SrcScalarType, bool SrcSafe,typename DestScalarType, bool DestSafe>
inline void convolution(const Image<Channels,SrcScalarType,SrcSafe> &source,Image<Channels,DestScalarType,DestSafe> &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<DestScalarType>(source.getValueAsClamped(mx,my,channel));
destination.setValue(x,y,channel,sum);
} // colonne immagine
} // righe immagine
} // canali immagine
}
template<int Channels, typename ScalarType, bool Safe> // get[filter]ed() functions constrain return type to be the same of the parameter
inline Image<Channels,ScalarType,Safe> getConvolved(const Image<Channels,ScalarType,Safe> &image,const ScalarType *matrix,int matrix_width,int matrix_height)
{
Image<Channels,ScalarType,Safe> i;
convolution(image,i,matrix,matrix_width,matrix_height);
return i;
}
template<int Channels, typename SrcScalarType, bool SrcSafe,typename DestScalarType, bool DestSafe>
inline void boxFilter(const Image<Channels,SrcScalarType,SrcSafe> &source,Image<Channels,DestScalarType,DestSafe> &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<matrix_size; i++)
matrix[i]=val;
convolution(source,destination,matrix,matrix_side,matrix_side);
delete [] matrix;
}
template<int Channels, typename ScalarType, bool Safe> // get[filter]ed() functions constrain return type to be the same of the parameter
inline Image<Channels,ScalarType,Safe> getBoxFiltered(const Image<Channels,ScalarType,Safe> &image,int radius)
{
Image<Channels,ScalarType,Safe> i;
boxFilter(image,i,radius);
return i;
}
template<typename ScalarType>
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<matrix_size;i++)
sum+=matrix[i];
for(int i=0;i<matrix_size;i++)
matrix[i]/=sum;
}
template<int Channels, typename SrcScalarType, bool SrcSafe,typename DestScalarType, bool DestSafe>
inline void GaussianSmooth(const Image<Channels,SrcScalarType,SrcSafe> &source,Image<Channels,DestScalarType,DestSafe> &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<int Channels, typename ScalarType, bool Safe> // get[filter]ed() functions constrain return type to be the same of the parameter
inline Image<Channels,ScalarType,Safe> getGaussianSmoothed(const Image<Channels,ScalarType,Safe> &image,const int radius)
{
Image<Channels,ScalarType,Safe> i;
GaussianSmooth(image,i,radius);
return i;
}
template<int Channels, typename SrcScalarType, bool SrcSafe,typename DestScalarType, bool DestSafe>
inline void LaplacianFilter(const Image<Channels,SrcScalarType,SrcSafe> &source,Image<Channels,DestScalarType,DestSafe> &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<int Channels, typename ScalarType, bool Safe> // get[filter]ed() functions constrain return type to be the same of the parameter
inline Image<Channels,ScalarType,Safe> getLaplacianFiltered(const Image<Channels,ScalarType,Safe> &image)
{
Image<Channels,ScalarType,Safe> i;
LaplacianFilter(image,i);
return i;
}
template<typename ScalarType>
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<matrix_size;i++)
sum+=matrix[i];
for(int i=0;i<matrix_size;i++)
matrix[i]/=sum;
}
template<int Channels, typename SrcScalarType, bool SrcSafe,typename DestScalarType, bool DestSafe>
inline void LoGFilter(const Image<Channels,SrcScalarType,SrcSafe> &source,Image<Channels,DestScalarType,DestSafe> &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<int Channels, typename ScalarType, bool Safe> // get[filter]ed() functions constrain return type to be the same of the parameter
inline Image<Channels,ScalarType,Safe> getLoGFiltered(const Image<Channels,ScalarType,Safe> &image,int radius)
{
Image<Channels,ScalarType,Safe> i;
LoGFilter(image,i,radius);
return i;
}
template<int Channels, typename SrcScalarType, bool SrcSafe,typename DestScalarType, bool DestSafe>
inline void DoGFilter(const Image<Channels,SrcScalarType,SrcSafe> &source,Image<Channels,DestScalarType,DestSafe> &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<matrix_size;i++)
m1[i] -= m2[i];
// riporto la matrice a somma 1
DestScalarType sum=0.0f;
for(int i=0;i<matrix_size;i++)
sum+=m1[i];
for(int i=0;i<matrix_size;i++)
m1[i]/=sum;
convolution(source,destination,m1,matrix_side,matrix_side);
delete [] m1;
delete [] m2;
}
template<int Channels, typename ScalarType, bool Safe> // get[filter]ed() functions constrain return type to be the same of the parameter
inline Image<Channels,ScalarType,Safe> getDoGFiltered(const Image<Channels,ScalarType,Safe> &image,int radius1,int radius2)
{
Image<Channels,ScalarType,Safe> i;
DoGFilter(image,i,radius1,radius2);
return i;
}
template<int Channels, typename SrcScalarType, bool SrcSafe,typename DestScalarType, bool DestSafe>
inline void UnsharpMask(const Image<Channels,SrcScalarType,SrcSafe> &source,Image<Channels,DestScalarType,DestSafe> &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<length;offset++)
destination_array[offset] = source_array[offset]+factor*(source_array[offset]-destination_array[offset]);
}
template<int Channels, typename ScalarType, bool Safe> // get[filter]ed() functions constrain return type to be the same of the parameter
inline Image<Channels,ScalarType,Safe> getUnsharpMasked(const Image<Channels,ScalarType,Safe> &image,int radius,float factor)
{
Image<Channels,ScalarType,Safe> i;
UnsharpMask(image,i,radius,factor);
return i;
}
template<int Channels, typename SrcScalarType, bool SrcSafe,typename DestScalarType, bool DestSafe>
inline void medianFilter(const Image<Channels,SrcScalarType,SrcSafe> &source,Image<Channels,DestScalarType,DestSafe> &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<DestScalarType> 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<DestScalarType>(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<int Channels, typename ScalarType, bool Safe> // get[filter]ed() functions constrain return type to be the same of the parameter
inline Image<Channels,ScalarType,Safe> getMedianFiltered(const Image<Channels,ScalarType,Safe> &image,int radius)
{
Image<Channels,ScalarType,Safe> i;
medianFilter(image,i,radius);
return i;
}
template<int Channels, typename ScalarType1, bool Safe1,typename ScalarType2, bool Safe2>
inline void channels_mean(const img::Image<Channels,ScalarType1,Safe1> &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; ++channel)
sum += static_cast<ScalarType2>(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_*/

512
img/img_image.h Executable file
View File

@ -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<int Channels=3, typename ScalarType=double, bool Safe=true>
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<ScalarType> 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<Channels,ScalarType,Safe> &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<typename OtherScalarType, bool OtherSafe>
Image(const Image<Channels,OtherScalarType,OtherSafe> &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<ScalarType>(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<typename OtherScalarType, bool OtherSafe>
inline Image< Channels,ScalarType,Safe> & operator =(const Image<Channels,OtherScalarType,OtherSafe> &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<ScalarType>(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<Channels;++channel)
ret_pixel[channel] = _data[ (x + y * _width) * Channels + channel ];
}
/*! \brief set all the values of a pixel
\param x the horizontal coordinate of the pixel
\param y the vertical coordinate of the pixel
\param pixel the pixel values that are assigned to the pixel
*/
inline void setPixel(int x, int y, const ScalarType (& pixel)[Channels])
{
assert( _data != NULL );
assert( x >= 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<Channels;++channel)
_data[ (x + y * _width) * Channels + channel ] = pixel[channel];
}
/*! \brief get 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
\return the value of the channel at the pixel
*/
inline ScalarType getValue(int x, int y, int channel) const
{
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("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<int>(floor(x+0.5f)),static_cast<int>(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<int>(floor(x));
// int y1 = static_cast<int>(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_*/

45
img/img_info.h Executable file
View File

@ -0,0 +1,45 @@
#ifndef IMG_INFO_H_
#define IMG_INFO_H_
// functions that extrapolate information from read-only images
namespace img {
template<int Channels,typename ScalarType, bool Safe>
inline ScalarType minValue(const Image<Channels,ScalarType,Safe> &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<length;offset++)
if(min > array[offset])
min = array[offset];
return min;
}
template<int Channels,typename ScalarType, bool Safe>
inline ScalarType maxValue(const Image<Channels,ScalarType,Safe> &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<length;offset++)
if(max < array[offset])
max = array[offset];
return max;
}
} //end namespace img
#endif /*IMG_INFO_H_*/

84
img/img_io.h Executable file
View File

@ -0,0 +1,84 @@
#ifndef IMG_IO_H_
#define IMG_IO_H_
// minimal input/output
#include <iostream>
#include <fstream>
#include <limits>
namespace img {
template<int Channels, typename ScalarType, bool Safe>
inline bool saveNativeFormat(const Image<Channels,ScalarType,Safe> &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<const char*>(& 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<const char*>(& scalartype), sizeof(int));
int width=image.width();
output.write(reinterpret_cast<const char*>(& width), sizeof(int));
int height=image.height();
output.write(reinterpret_cast<const char*>(& height), sizeof(int));
output.write(reinterpret_cast<const char*>(& image.attributes), sizeof(ImgAttributes<ScalarType>));
output.write(reinterpret_cast<const char*>(image.dataValues()), sizeof(ScalarType) * image.dataValuesSize());
output.flush();
output.close();
return true;
}
if(Safe)
throw ImageException("Unable to save file");
return false;
}
template<int Channels, typename ScalarType, bool Safe>
inline bool openNativeFormat(const char *filename, Image<Channels,ScalarType,Safe> &image)
{
using namespace std;
ifstream input (filename, ios::in|ios::binary);
if (input.is_open()) {
int channels;
input.read(reinterpret_cast<char*>(&channels), sizeof(int));
assert(channels==Channels);
int scalartype;
input.read(reinterpret_cast<char*>(&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<char*>(&width), sizeof(int));
int height;
input.read(reinterpret_cast<char*>(&height), sizeof(int));
image.setZero(width,height);
input.read(reinterpret_cast<char*>(& image.attributes), sizeof(ImgAttributes<ScalarType>));
input.read(reinterpret_cast<char*>(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_*/

108
img/img_scalar.h Executable file
View File

@ -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<typename ScalarType>
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<typename ScalarType>
inline int valueAsInt(ScalarType value)
{
STATIC_FLOAT_OR_DOUBLE_TYPECHECK(ScalarType);
return static_cast<int>(floor(static_cast<double>(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<typename ScalarType>
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<typename ScalarType>
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<typename ScalarType>
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<typename ScalarType>
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_*/