eLynx SDK
v3.3.0 C++ image processing API reference |
Table of contents
Many image formats are supported with multiple resolutions as show in the table:
Image types | Resolutions in bits | |||||
---|---|---|---|---|---|---|
integer | float | |||||
8 | 16 | 32 | 32 | 64 | ||
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.
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");
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.
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");
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); }