eLynx SDK
v3.3.0 C++ image processing API reference |
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.
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.
The solution is simple: write the service that need the less global amount of memory.
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; }
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); }
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; }
In sdk we work with eLynx::Image::AbstractImage, eLynx::Image::ImageImpl and eLynx::Image::ImageVariant. Each image type have different constraints.