eLynx SDK
v3.0.1 C++ image processing API reference |
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 (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");
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");
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.
There is 35 pixel specializations:
// 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");
The reason is due to software architecture for efficiently handle different points :
We have tree different levels of services:
We get 35 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_XYZ case PF_XYZf: return boost::shared_ptr<AbstractImage>( new ImageXYZf( (const ImageXYZf&)iImage ) ); case PF_XYZd: return boost::shared_ptr<AbstractImage>( new ImageXYZd( (const ImageXYZd&)iImage ) ); // PF_Luv case PF_Luvf: return boost::shared_ptr<AbstractImage>( new ImageLuvf( (const ImageLuvf&)iImage ) ); case PF_Luvd: return boost::shared_ptr<AbstractImage>( new ImageLuvd( (const ImageLuvd&)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_Lch case PF_Lchf: return boost::shared_ptr<AbstractImage>( new ImageLchf( (const ImageLchf&)iImage ) ); case PF_Lchd: return boost::shared_ptr<AbstractImage>( new ImageLchd( (const ImageLchd&)iImage ) ); // PF_HLab case PF_HLabf: return boost::shared_ptr<AbstractImage>( new ImageHLabf( (const ImageHLabf&)iImage ) ); case PF_HLabd: return boost::shared_ptr<AbstractImage>( new ImageHLabd( (const ImageHLabd&)iImage ) ); // PF_CPLX case PF_CPLXi: return boost::shared_ptr<AbstractImage>( new ImageComplexi( (const ImageComplexi&)iImage ) ); case PF_CPLXf: return boost::shared_ptr<AbstractImage>( new ImageComplexf( (const ImageComplexf&)iImage ) ); case PF_CPLXd: return boost::shared_ptr<AbstractImage>( new ImageComplexd( (const ImageComplexd&)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; static ImageHandlerImpl<PixelComplexi> s_HandlerCPLXi; static ImageHandlerImpl<PixelComplexf> s_HandlerCPLXf; static ImageHandlerImpl<PixelComplexd> s_HandlerCPLXd; static ImageHandlerImpl<PixelHLSf> s_HandlerHLSf; static ImageHandlerImpl<PixelHLSd> s_HandlerHLSd; static ImageHandlerImpl<PixelXYZf> s_HandlerXYZf; static ImageHandlerImpl<PixelXYZd> s_HandlerXYZd; static ImageHandlerImpl<PixelLuvf> s_HandlerLuvf; static ImageHandlerImpl<PixelLuvd> s_HandlerLuvd; static ImageHandlerImpl<PixelLabf> s_HandlerLabf; static ImageHandlerImpl<PixelLabd> s_HandlerLabd; static ImageHandlerImpl<PixelLchf> s_HandlerLchf; static ImageHandlerImpl<PixelLchd> s_HandlerLchd; static ImageHandlerImpl<PixelHLabf> s_HandlerHLabf; static ImageHandlerImpl<PixelHLabd> s_HandlerHLabd; //---------------------------------------------------------------------------- // Defined in same order as in EPixelFormat declaration //---------------------------------------------------------------------------- static const IImageHandler * s_prImageTypeHandler_lut[PF_Undefined] = { // ubyte ushort 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, &s_HandlerCPLXi, &s_HandlerCPLXf, &s_HandlerCPLXd, &s_HandlerHLSf, &s_HandlerHLSd, &s_HandlerXYZf, &s_HandlerXYZd, &s_HandlerLuvf, &s_HandlerLuvd, &s_HandlerLabf, &s_HandlerLabd, &s_HandlerLchf, &s_HandlerLchd, &s_HandlerHLabf, &s_HandlerHLabd }; //---------------------------------------------------------------------------- // 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); }