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

How to implement an image processing method?

Explanations are based on illustrations of real implementor life.


How to implement high level services: flip vertical, resize and change pixel format.
Theses functionnalities show us most of problems with image processing and give solutions for eLynx sdk.

// high level
bool bSuccess = false;
ImageVariant image("jupiter.jpg");
bSuccess = image.FlipVertical();
bSuccess = image.Resize(1000,1000);
bSuccess = image.ChangePixelFormat(PF_RGBf);
bSuccess = image.Save("jupiter.fit");

Long is the road from low to high levels implementation but powerfull is the result for end user.

  1. General naming for image services
  2. Where to declare new image services?
  3. Low level, ImageImpl implementation
  4. Middle level, ImageImpl and AbstractImage implementation
  5. Generalized level, AbstractImage implementation
  6. High level, ImageVariant implementation

General naming for image services

If we don't care about image type, say we have Image class.
For each image operations we can write two services:

In eLynx sdk we'll write these two services for most of operations.
Note that we do not duplicated code for both services.

So what's service to implement? Process or CreateProcessed?

The solution is simple: write the service that need the less global amount of memory.

If we CAN do in-place processing

Implement

bool Process(Image& ioImage, parameters)
{
  // transforms ioImage map to ioImage map
  ...
}

and write

boost::shared_ptr<Image> CreateProcessed(const Image& iImage, parameters)
{
  // Clone image
  boost::shared_ptr<Image> spImage = elxCreateImage(iImage);
  
  // transform cloned inplace
  Process(*spImage, parameters);
  
  // return transformed cloned
  return spImage;
}

If we CAN'T do in-place processing

Implement

boost::shared_ptr<Image> CreateProcessed(const Image& iImage, parameters)
{
  // Create new image
  boost::shared_ptr<Image> spImage = call to an image creation factory(...);
  
  // My implementation transforms iImage map to spImage map
  ...
  
  // return the created image
  return spImage;
}

and write

bool Process(Image& ioImage, parameters)
{
  boost::shared_ptr<Image> spImage = CreateTransformed(ioImage, parameters);
  return ioImage.CopyAndForget(spImage);
}

Where to declare new image services?

New services must be declared enriching existing interfaces:

In our example, let's add flip vertical service.

In IImageGeometry.h we add :

class IImageGeometry
{
public:
  ...
  virtual bool Flip(AbstractImage& ioImage, EFlipPlane iFlipPlane) const = 0;
  ...
  virtual boost::shared_ptr<AbstractImage> CreateFlipped(const AbstractImage& iImage, EFlipPlane iFlipPlane) const = 0;
};

In ImageGeometryImpl.h we add :

template < class Pixel >
class ImageGeometryImpl : public IImageGeometry
{
public:
  // IImageGeometry implementation, Middle level services
  virtual bool Flip(AbstractImage& ioImage, EFlipPlane iFlipPlane) const;
  virtual boost::shared_ptr<AbstractImage> CreateFlipped(const AbstractImage& iImage, EFlipPlane iFlipPlane) const;
  ...
  // Low level services
  static bool FlipVertical(ImageImpl<Pixel>&);
  static bool Flip(ImageImpl<Pixel>&, EFlipPlane);
  ...
  static boost::shared_ptr< ImageImpl<Pixel> > CreateFlipped(const ImageImpl<Pixel>&, EFlipPlane);
};
...
#include "Inl/Geometry/Flip.inl"
...

In Flip.inl we write:

namespace eLynx {
namespace Image {

//<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
//                    static specialized services
//<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>

//----------------------------------------------------------------------------
//  FlipVertical : perform vertical symmetry of image
//----------------------------------------------------------------------------
//  public static
//----------------------------------------------------------------------------
//  In  : ImageImpl<Pixel>& ioImage
//  Out : bool : success status
//----------------------------------------------------------------------------
template <class Pixel>
bool ImageGeometryImpl<Pixel>::FlipVertical(ImageImpl<Pixel>& ioImage)
{
  if (!ioImage.IsValid())
    return false;

  const uint h = ioImage.GetHeight();
  const uint w = ioImage.GetWidth();
  const uint bytesWidth = ioImage.sizeofWidth();

  Pixel_t * plBuffer = new Pixel_t[w];
  if (NULL == plBuffer)
    return false;

  Pixel_t * prTop = ioImage.GetPixel();
  Pixel_t * prBottom = prTop + w*(h-1);

  const uint Half = h/2;
  for (uint i=0; i<Half; ++i)
  {
    // memcpy deals with size in byte
    ::memcpy(plBuffer, prTop,    bytesWidth);
    ::memcpy(prTop,    prBottom, bytesWidth);
    ::memcpy(prBottom, plBuffer, bytesWidth);

    prTop += w;
    prBottom -= w;
  }

  elxSAFE_DELETE_LIST(plBuffer);
  return true;

} // FlipVertical

//----------------------------------------------------------------------------
template <class Pixel>
bool ImageGeometryImpl<Pixel>::Flip(ImageImpl<Pixel>& ioImage,
    EFlipPlane iFlipPlane)
{
  if (!ioImage.IsValid())
    return false;

  switch(iFlipPlane)
  {
    case FP_Horizontal: return FlipHorizontal(ioImage);
    ...
  }
  return false;

} // Flip

//----------------------------------------------------------------------------
//  CreateFlipped
//----------------------------------------------------------------------------
template <typename Pixel>
boost::shared_ptr< ImageImpl<Pixel> > ImageGeometryImpl<Pixel>::CreateFlipped(
    const ImageImpl<Pixel>& iImage,
    EFlipPlane iFlipPlane)
{
  boost::shared_ptr< ImageImpl<Pixel> > spImage(new ImageImpl<Pixel>(iImage));
  Flip(*spImage, iFlipPlane);
  return spImage;

} // CreateFlipped


//<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>
//            virtual from IImageGeometry implementation
//<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>

//----------------------------------------------------------------------------
//  Flip : perform symmetry of image
//----------------------------------------------------------------------------
//  public virtual from IImageGeometry
//----------------------------------------------------------------------------
//  In  : AbstractImage& ioImage
//        EFlipPlane iFlipPlane
//  Out : bool : success status
//----------------------------------------------------------------------------
template <class Pixel>
bool ImageGeometryImpl<Pixel>::Flip(AbstractImage& ioImage, EFlipPlane iFlipPlane) const
{
  ImageImpl<Pixel>& image = elxDowncast<Pixel>(ioImage);
  return Flip(image, iFlipPlane);

} // Flip

//----------------------------------------------------------------------------
//  CreateFlipped
//----------------------------------------------------------------------------
//  public virtual from IImageGeometry
//----------------------------------------------------------------------------
template <typename Pixel>
boost::shared_ptr<AbstractImage> ImageGeometryImpl<Pixel>::CreateFlipped(
    const AbstractImage& iImage,
    EFlipPlane iFlipPlane) const
{
  const ImageImpl<Pixel>& image = elxDowncast<Pixel>(iImage);
  return CreateFlipped(image, iFlipPlane);

} // CreateFlipped

} // namespace Image
} // namespace eLynx

Generalized level: AbstractImage domain.
In AbstractImageManager.h we add:

...
// Perform vertical symmetry of an image.
// @param ioImage image to process.
// @return true if succeeded, false otherwise.
ExportedByImage bool elxFlipVertical(AbstractImage& ioImage);
...

In AbstractImageManager.cpp we add:

...
bool elxFlipVertical(AbstractImage& ioImage)
{
  return elxGetGeometryHandler(ioImage).Flip(ioImage, FP_Vertical); 
}
...

This simple way we handle FlipVertical for all 33 image types!

High level: ImageVariant domain.
In ImageVariant.h we declare method:

// Perform vertical symmetry of an image.
// @return Method running status.
bool FlipVertical();

In ImageVariant.cpp we implement method:

bool ImageVariant::FlipVertical()
{
  if (NULL == _spAbstractImpl.get()) return false;
  const bool bSuccess = elxGetGeometryHandler(*_spAbstractImpl).
    Flip(*_spAbstractImpl, FP_Vertical);

  _Bayer = elxFlipVertical(_Bayer, GetHeight());
  return bSuccess;
}

Low level, ImageImpl implementation


Middle level, ImageImpl and AbstractImage implementation


Generalized level, AbstractImage implementation


High level, ImageVariant implementation


eLynx point of view with sdk image types

In sdk we work with eLynx::Image::AbstractImage, eLynx::Image::ImageImpl and eLynx::Image::ImageVariant. Each image type have different constraints.


Generated on Thu Dec 9 2010 by doxygen 1.7.2