Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A better way to handle template creation?

Trying to figure out a better design for this!

Consider that we have a template Image class that inherits from a template matrix library (in this case Eigen but it could be anything)

template <typename T>
class Image : public Eigen::Matrix <T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>

Now think of the case that we want to write a function to handle reading images from a file. Of course images can be of different types, i.e. unisnged char, uint16_t, float and even have different channels as in grayscale, RGB or even RGBA.

So we could have of course template classes to easily handle this.As in

Image<RGB<unisgned char>> or Image<RGBA<float>>

Its simple when one knows the type of the image, say monochrome 8bit

Image<unisgned char> image = ReadImage(const char* const filename);

or it could even be

Image<unisgned char> image;
bool b = ReadImage(const char* const filename, Image<unisgned char>& image)

However when reading an image file we never know the type prior to reading the image.For example Tiff and png both support 8bit and 16 bit with tiff even supporting float. In such cases its impossible to use any of the functions mentioned above. However, we can have a temmplate Factory class to sort that out.

For that we first need to introduce a BaseImage class

class BaseImage
{
public:
    inline BaseImage() {};
    virtual inline ~BaseImage() {};

    virtual inline int Width() const = 0;
    virtual inline int Height() const = 0;
    virtual inline int Depth() const = 0;
    etc...
};

template <typename T>
class Image : public BaseImage, public Eigen::Matrix <T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>

Then we can have our factory class where we pass our unsigned char*, float*, etc.. and let it handle the creation

class ImageFactory
{
  typename <T>
  static BaseImage* createImage(const T* const src, const int& width, const int& height, const int& depth)
  {
    if (depth == 1)
    {
      return new Image<T>();
    }
    else if (depth == 3)
    {
      return new Image<RGB<T>>();
    }
    etc...
  }
}

Of course this forces me to use dynamic allocation and inheritance.

I guess I can get around the dynamic allocation by hiding it inside a class that takes care of it. In the constructor the factory clas will be called

class Image2
{
public:
    Image2(const char* const path)
    {
        // where ReadImage and external function that will call ImageFactory
        pBaseImage = ReadImage(path);
    }
    ~Image2();

private:
    BaseImage* pBaseImage;
};

In either case my BaseImage class will have to expose all the functionality that I need to use from my matrix library which kinda defies the purpose of inheriting.

The question is then if there is a better design than could be used here cause its becoming quite cumbersome

like image 242
xerion Avatar asked Sep 23 '14 08:09

xerion


1 Answers

I'd post this as a comment if I could show code in a comment ... to me, this seems obvious enough, and you probably thought of it. Why isn't it what you're looking for?

class Image
{
public:
  Image (int bytesPerPixelIn = 1) { data_ = NULL; bytesPerPixel=bytesPerPixelIn;};

  void readImage (int width, int height);
private:
  int       bytesPerPixel_;
  void*     data_;
};

void Image::read (int width, int height, ...)
{
   data_ = new unsigned char [width*height*pixelSize(imageType_)];
   //...
}

//And you can inherit from this if need be to add special functions 
//like interleaving in BMP files

Edit: OK, I see. Would this serve better?

template <typename PixelType>
class Image
{
public:
  Image () { data_ = NULL; };

  virtual void allocateImage (int width, int height)
  {
    data_ = new PixelType [width*height]; 
  }
  virtual void readImage (int width, int height) = 0;
private:
  PixelType*     data_;
};

template <typename PixelType>
class ImageTIFF: public Image <PixelType> ...

or this:

template <typename PixelType, int WIDTH, int HEIGHT>
class Image
{
public:
  Image () { }

  virtual void readImage () = 0;
private:
  PixelType*     data_ [WIDTH][HEIGHT];
};

template <typename PixelType, int WIDTH, int HEIGHT>
class ImageTIFF: public Image <PixelType, WIDTH, HEIGHT> ...

The latter eliminates dynamic memory... but without dynamic memory, you can't have a resize function. I'd use dynamic memory.

You'll need inheritance for different file formats, surely, as they don't read images the same way.

--

OK. How about this? BaseImage here is like a Java "interface," and will ensure that the derived classes have the functions they'll need.

class BaseImage 
{
public:
  virtual void allocateImage (int width, int height) = 0;
  virtual void readImage     (int width, int height) = 0;
};

template <typename PixelType>
class Image: public BaseImage ... /* and so on */

BaseImage* myTIFFImage = readTIFFImage (/* whatever goes here */);
like image 162
Topological Sort Avatar answered Sep 30 '22 16:09

Topological Sort