eLynx SDK v3.3.0
C++ image processing API reference

Image software architecture

Table of contents


Images type overview

Many image formats are supported with multiple resolutions as show in the table:

Image
types
Resolutions in bits
integerfloat
816323264
Gray Yes Yes Yes Yes Yes
Gray+Alpha Yes Yes Yes Yes Yes
Gray+Bayer Yes Yes Yes Yes Yes
RGB Yes Yes Yes Yes Yes
RGB+Alpha Yes Yes Yes Yes Yes
HLS No No No Yes Yes
CIE Lab No No No Yes Yes

Formats and resolutions complexity is hidden for the user, using the ImageVariant utility class. It's a bridge pattern that hides the implementations of the AbstractImage class. Image's pixel format is known at run-time. It can change during the image life cycle.


Image class definitions

ImageVariant

eLynx::Image::ImageVariant is an high level image definition. It uses the bridge pattern to hide the implementation of the AbstractImage class for clients. With ImageVariant, the pixel format is known at run-time. Pixel format can change during image's life cycle. It's also an wrapper for all AbstractImage services. It introduces the color field array concept, see Bayer matrix.

// high level
bool bSuccess = false;

// Load an image, the pixel format depends of image file format
ImageVariant image("../../datas/jupiter.jpg");

// do some processing
bSuccess = image.FlipVertical();
bSuccess = image.Resize(1000,1000);
bSuccess = image.ChangePixelFormat(PF_RGBf);

// save the image
bSuccess = image.Save("jupiter.fit");

AbstractImage

eLynx::Image::AbstractImage define an generic pixel container at middle level. It can't be instanciate as it containts pure virtual methods. It allows to use image object where pixel format is known at runtime (implementation is hidden).

// mid level
bool bSuccess = false;

// Load an image, the pixel format depends of image file format
ImageVariant image("../../datas/jupiter.jpg");

// Get the ImageVariant hidden implementation as an AbstractImage
boost::shared_ptr<AbstractImage> spAbstractImage = image.GetImpl();

// Do some processing with AbstractImage
const IImageGeometry& geometryHandler = elxGetGeometryHandler(*spAbstractImage);
geometryHandler.Flip(*spAbstractImage, FP_Vertical);

boost::shared_ptr<AbstractImage> spAbstractImage2 = 
  geometryHandler.CreateResized(*spAbstractImage, 1000,1000);

boost::shared_ptr<AbstractImage> spAbstractImage3 = 
  elxCreateImage(*spAbstractImage2, PF_RGBf);

image.Assign(spAbstractImage3);
bSuccess = image.Save("jupiter2.fit");

ImageImpl< Pixel >

eLynx::Image::ImageImpl is the template class that implements AbstractImage at low level. It's a specialized image by his pixel format that can be instanciate. This is a specialized pixel container, it has data descriptions and accessors but no processing method.

Here are the implemented pixel specializations for image:

// low level
bool bSuccess = false;
ImageVariant image("../../datas/jupiter.jpg");
boost::shared_ptr<AbstractImage> spAbstractImage = image.GetImpl();
        
boost::shared_ptr<ImageRGBub> spImageRGBub = 
  dynamic_pointer_cast<ImageImpl<PixelRGBub>, AbstractImage>(spAbstractImage);

bSuccess = ImageGeometryImpl<PixelRGBub>::FlipVertical( *spImageRGBub );

boost::shared_ptr<ImageRGBub> spImageRGBub2 = 
  ImageGeometryImpl<PixelRGBub>::CreateResized(*spImageRGBub, 1000,1000);

boost::shared_ptr<AbstractImage> spImageRGBf = 
  elxCreateImageRGBf( *spImageRGBub2 );
 
image.Assign( spImageRGBf );
bSuccess = image.Save("jupiter3.fit");

Go to top


Image classes architecture

ImageArchi.png

Go to top


Image service levels

Why do we need three image class definitions?

The reason is due to software architecture for efficiently handle different points :

We have tree different levels of services:

We get a lot of image class implemementations, so we can't think about implementing methods for every classes. We decide to uses templates as generalization tool. We restrict AbstractImage and ImageImpl< Pixel > to be pixel container and no more. This way a image service is writing out of the image class.

Suppose we want to write basic cloning service:
AbstractImage * elxCreateImage(const AbstractImage& iImage);

We can do:

//----------------------------------------------------------------------------
//  copy constructor
//----------------------------------------------------------------------------
//  public
//----------------------------------------------------------------------------
//  In  : const ImageImpl& iImage: image to copy
//----------------------------------------------------------------------------
template <class Pixel>
inline
ImageImpl<Pixel>::ImageImpl(const ImageImpl& iImage) :
    AbstractImage(iImage), 
  _spMap()
{
  if (!iImage.IsValid())
    return;

  const uint nPixel = GetPixelCount();
  if (0 != nPixel)
  {
    // copy map
    _spMap.reset( new Pixel_t[nPixel] );
    ::memcpy(_spMap.get(), iImage._spMap.get(), sizeofMap());
  }
    
} // copy constructor
//----------------------------------------------------------------------------
//  elxCreateImage : abstract image factory from image 
//----------------------------------------------------------------------------
//  global
//----------------------------------------------------------------------------
boost::shared_ptr<AbstractImage> elxCreateImage(const AbstractImage& iImage)
{
  switch(iImage.GetPixelFormat())
  {
    // PF_RGB
    case PF_RGBub:  return boost::shared_ptr<AbstractImage>( new ImageRGBub( (const ImageRGBub&)iImage ) );
    case PF_RGBus:  return boost::shared_ptr<AbstractImage>( new ImageRGBus( (const ImageRGBus&)iImage ) );
    case PF_RGBi:   return boost::shared_ptr<AbstractImage>( new ImageRGBi(  (const ImageRGBi&)iImage ) );
    case PF_RGBf:   return boost::shared_ptr<AbstractImage>( new ImageRGBf(  (const ImageRGBf&)iImage ) );
    case PF_RGBd:   return boost::shared_ptr<AbstractImage>( new ImageRGBd(  (const ImageRGBd&)iImage ) );

    // PF_L
    case PF_Lub:    return boost::shared_ptr<AbstractImage>( new ImageLub( (const ImageLub&)iImage ) );
    case PF_Lus:    return boost::shared_ptr<AbstractImage>( new ImageLus( (const ImageLus&)iImage ) );
    case PF_Li:     return boost::shared_ptr<AbstractImage>( new ImageLi(  (const ImageLi&)iImage ) );
    case PF_Lf:     return boost::shared_ptr<AbstractImage>( new ImageLf(  (const ImageLf&)iImage ) );
    case PF_Ld:     return boost::shared_ptr<AbstractImage>( new ImageLd(  (const ImageLd&)iImage ) );

    // PF_HLS
    case PF_HLSf:   return boost::shared_ptr<AbstractImage>( new ImageHLSf( (const ImageHLSf&)iImage ) );
    case PF_HLSd:   return boost::shared_ptr<AbstractImage>( new ImageHLSd( (const ImageHLSd&)iImage ) );

    // PF_Lab
    case PF_Labf:   return boost::shared_ptr<AbstractImage>( new ImageLabf( (const ImageLabf&)iImage ) );
    case PF_Labd:   return boost::shared_ptr<AbstractImage>( new ImageLabd( (const ImageLabd&)iImage ) );

    // PF_LA
    case PF_LAub:    return boost::shared_ptr<AbstractImage>( new ImageLAub( (const ImageLAub&)iImage ) );
    case PF_LAus:    return boost::shared_ptr<AbstractImage>( new ImageLAus( (const ImageLAus&)iImage ) );
    case PF_LAi:     return boost::shared_ptr<AbstractImage>( new ImageLAi(  (const ImageLAi&)iImage ) );
    case PF_LAf:     return boost::shared_ptr<AbstractImage>( new ImageLAf(  (const ImageLAf&)iImage ) );
    case PF_LAd:     return boost::shared_ptr<AbstractImage>( new ImageLAd(  (const ImageLAd&)iImage ) );

    // PF_RGBA
    case PF_RGBAub:  return boost::shared_ptr<AbstractImage>( new ImageRGBAub( (const ImageRGBAub&)iImage ) );
    case PF_RGBAus:  return boost::shared_ptr<AbstractImage>( new ImageRGBAus( (const ImageRGBAus&)iImage ) );
    case PF_RGBAi:   return boost::shared_ptr<AbstractImage>( new ImageRGBAi(  (const ImageRGBAi&)iImage ) );
    case PF_RGBAf:   return boost::shared_ptr<AbstractImage>( new ImageRGBAf(  (const ImageRGBAf&)iImage ) );
    case PF_RGBAd:   return boost::shared_ptr<AbstractImage>( new ImageRGBAd(  (const ImageRGBAd&)iImage ) );

    default:
      return boost::shared_ptr<AbstractImage>();
  }

} // elxCreateImage

We decide NOT to have such a code for middle level. Having switch(iImage.GetPixelFormat()) with all formats give us hard work writing and maintening code while we can do simplier. We define an interface with middle level services that must be implemented for all image type: eLynx::Image::IImageHandler.

class ExportedByImage IImageHandler
{
public:
  virtual ~IImageHandler();

  virtual boost::shared_ptr<AbstractImage> 
    CreateImage(uint iWidth, uint iHeight) const = 0;

  virtual boost::shared_ptr<AbstractImage> 
    CreateImage(const AbstractImage& iImage) const = 0;

  virtual boost::shared_ptr<AbstractImage> 
    CreateImage(const AbstractImage& iImage, 
      EResolution iResolution, bool ibScaled = true) const = 0; 
      
  virtual boost::shared_ptr<AbstractImage> 
    CreateImageUByteFullDynamic(const AbstractImage& iImage) const = 0;

  virtual const IImageGeometry& GetGeometryHandler() const = 0;
  virtual const IImageAnalyse& GetAnalyseHandler() const = 0;
  virtual const IImageOperators& GetOperatorsHandler() const = 0;
  virtual const IImagePointProcessing& GetPointToPointHandler() const = 0;
  virtual const IImageLocalProcessing& GetLocalToPointHandler() const = 0;
  virtual const IImageGlobalProcessing& GetGlobalToPointHandler() const = 0;
  virtual const IImageEdgeProcessing& GetEdgeProcessingHandler() const = 0;
  virtual const IImageMorphologicalProcessing& GetMorphologicalHandler() const = 0;
};

The class defining an implementation of image hander interface:

template < class Pixel >
class ImageHandlerImpl : public IImageHandler
{
public:
  virtual boost::shared_ptr<AbstractImage> 
    CreateImage(uint iWidth, uint iHeight) const;
    
  virtual boost::shared_ptr<AbstractImage> 
    CreateImage(const AbstractImage& iImage) const;
    
  virtual boost::shared_ptr<AbstractImage> 
    CreateImage(const AbstractImage& iImage, EResolution iResolution, bool ibScaled=true) const; 
    
  virtual boost::shared_ptr<AbstractImage> 
    CreateImageUByteFullDynamic(const AbstractImage& iImage) const;

  virtual const IImageGeometry& GetGeometryHandler() const;
  virtual const IImageAnalyse& GetAnalyseHandler() const;
  virtual const IImageOperators& GetOperatorsHandler() const;
  virtual const IImagePointProcessing& GetPointToPointHandler() const;
  virtual const IImageLocalProcessing& GetLocalToPointHandler() const;
  virtual const IImageGlobalProcessing& GetGlobalToPointHandler() const;
  virtual const IImageEdgeProcessing& GetEdgeProcessingHandler() const;
  virtual const IImageMorphologicalProcessing& GetMorphologicalHandler() const;
};

Now we can implement our cloning method mode dealing with all image types.
Note that we have an constraint on input AbstractImage:
AbstractImage has the same pixel type as < Pixel > template parameter.

//----------------------------------------------------------------------------
//  CreateImage : abstract image factory cloning image, ISO Pixel format
//----------------------------------------------------------------------------
//  public
//----------------------------------------------------------------------------
template < class Pixel >
boost::shared_ptr<AbstractImage> ImageHandlerImpl<Pixel>::CreateImage(
    const AbstractImage& iImage) const
{
  return boost::shared_ptr<AbstractImage> (
    new ImageImpl<Pixel>( static_cast< const ImageImpl<Pixel>& >(iImage)) );

} // CreateImage

In AbstractImageManager.h and AbstractImageManager.cpp we define and implement all AbstractImage services.

//----------------------------------------------------------------------------
// Instanciate all image type handlers
//----------------------------------------------------------------------------
static ImageHandlerImpl<PixelLub> s_HandlerLub;
static ImageHandlerImpl<PixelLus> s_HandlerLus;
static ImageHandlerImpl<PixelLi>  s_HandlerLi;
static ImageHandlerImpl<PixelLf>  s_HandlerLf;
static ImageHandlerImpl<PixelLd>  s_HandlerLd;

static ImageHandlerImpl<PixelLAub> s_HandlerLAub;
static ImageHandlerImpl<PixelLAus> s_HandlerLAus;
static ImageHandlerImpl<PixelLAi>  s_HandlerLAi;
static ImageHandlerImpl<PixelLAf>  s_HandlerLAf;
static ImageHandlerImpl<PixelLAd>  s_HandlerLAd;

static ImageHandlerImpl<PixelRGBub> s_HandlerRGBub;
static ImageHandlerImpl<PixelRGBus> s_HandlerRGBus;
static ImageHandlerImpl<PixelRGBi>  s_HandlerRGBi;
static ImageHandlerImpl<PixelRGBf>  s_HandlerRGBf;
static ImageHandlerImpl<PixelRGBd>  s_HandlerRGBd;

static ImageHandlerImpl<PixelRGBAub> s_HandlerRGBAub;
static ImageHandlerImpl<PixelRGBAus> s_HandlerRGBAus;
static ImageHandlerImpl<PixelRGBAi>  s_HandlerRGBAi;
static ImageHandlerImpl<PixelRGBAf>  s_HandlerRGBAf;
static ImageHandlerImpl<PixelRGBAd>  s_HandlerRGBAd;

#ifdef elxUSE_ImageComplex
static ImageHandlerImpl<PixelComplexi> s_HandlerCPLXi;
static ImageHandlerImpl<PixelComplexf> s_HandlerCPLXf;
static ImageHandlerImpl<PixelComplexd> s_HandlerCPLXd;
#endif

#ifdef elxUSE_ImageHLS
static ImageHandlerImpl<PixelHLSf> s_HandlerHLSf;
static ImageHandlerImpl<PixelHLSd> s_HandlerHLSd;
#endif

#ifdef elxUSE_ImageXYZ
static ImageHandlerImpl<PixelXYZf> s_HandlerXYZf;
static ImageHandlerImpl<PixelXYZd> s_HandlerXYZd;
#endif

#ifdef elxUSE_ImageLuv
static ImageHandlerImpl<PixelLuvf> s_HandlerLuvf;
static ImageHandlerImpl<PixelLuvd> s_HandlerLuvd;
#endif

#ifdef elxUSE_ImageLab
static ImageHandlerImpl<PixelLabf> s_HandlerLabf;
static ImageHandlerImpl<PixelLabd> s_HandlerLabd;
#endif

#ifdef elxUSE_ImageLch
static ImageHandlerImpl<PixelLchf> s_HandlerLchf;
static ImageHandlerImpl<PixelLchd> s_HandlerLchd;
#endif

#ifdef elxUSE_ImageHLab
static ImageHandlerImpl<PixelHLabf> s_HandlerHLabf;
static ImageHandlerImpl<PixelHLabd> s_HandlerHLabd;
#endif

//----------------------------------------------------------------------------
// Defined in same order as in EPixelFormat declaration
//----------------------------------------------------------------------------
static const IImageHandler * s_prImageTypeHandler_lut[PF_Undefined] =
{
//   uint8             uint16            integer           float            double
  &s_HandlerLub,    &s_HandlerLus,    &s_HandlerLi,     &s_HandlerLf,    &s_HandlerLd,
  &s_HandlerLAub,   &s_HandlerLAus,   &s_HandlerLAi,    &s_HandlerLAf,   &s_HandlerLAd,
  &s_HandlerRGBub,  &s_HandlerRGBus,  &s_HandlerRGBi,   &s_HandlerRGBf,  &s_HandlerRGBd,
  &s_HandlerRGBAub, &s_HandlerRGBAus, &s_HandlerRGBAi,  &s_HandlerRGBAf, &s_HandlerRGBAd,

#ifdef elxUSE_ImageComplex // ------------------------------------------------
                                      &s_HandlerCPLXi,  &s_HandlerCPLXf, &s_HandlerCPLXd,
#else
                                      NULL,             NULL,             NULL,
#endif
#ifdef elxUSE_ImageHLS // ----------------------------------------------------
                                                        &s_HandlerHLSf,  &s_HandlerHLSd,
#else
                                                        NULL,            NULL,
#endif
#ifdef elxUSE_ImageXYZ // ----------------------------------------------------
                                                        &s_HandlerXYZf,  &s_HandlerXYZd,
#else
                                                        NULL,            NULL,
#endif
#ifdef elxUSE_ImageLuv // ----------------------------------------------------
                                                        &s_HandlerLuvf,  &s_HandlerLuvd,
#else
                                                        NULL,            NULL,
#endif
#ifdef elxUSE_ImageLab // ----------------------------------------------------
                                                        &s_HandlerLabf,  &s_HandlerLabd,
#else
                                                        NULL,            NULL,
#endif
#ifdef elxUSE_ImageLch // ----------------------------------------------------
                                                        &s_HandlerLchf,  &s_HandlerLchd,
#else
                                                        NULL,            NULL,
#endif
#ifdef elxUSE_ImageHLab // ---------------------------------------------------
                                                        &s_HandlerHLabf, &s_HandlerHLabd
#else
                                                        NULL,            NULL
#endif
};

//----------------------------------------------------------------------------
//  elxGetImageHandler : return ImageManagerImpl for a given image type
//----------------------------------------------------------------------------
//  public global
//----------------------------------------------------------------------------
const IImageHandler& elxGetImageHandler(EPixelFormat iPixelFormat)
{
  elxASSERT(PF_Undefined != iPixelFormat);
  return *s_prImageTypeHandler_lut[iPixelFormat];
}
//----------------------------------------------------------------------------
const IImageHandler& elxGetImageHandler(const AbstractImage& iImage)
{
  return elxGetImageHandler( iImage.GetPixelFormat() );
}

//----------------------------------------------------------------------------
//  elxCreateImage : generic cloning
//----------------------------------------------------------------------------
//  global
//----------------------------------------------------------------------------
boost::shared_ptr<AbstractImage> elxCreateImage(const AbstractImage& iImage)
{
  return elxGetImageHandler(iImage).CreateImage(iImage);
}

Go to top


Generated on Thu Dec 9 2010 by doxygen 1.7.2