blImage — An image data structure based on shared_ptr and IplImage*

This entry is part 1 of 17 in the series blImageAPI -- BarbatoLabs Image API

Introduction

Back in the good old days of OpenCV 1.1 and earlier, the main image structure used throughout their algorithms was IplImage. Playing with IplImage requires skillful manipulation of data in memory, and thus makes it very difficult for beginners to play.  Even experts run into the dreaded memory leaks caused by an image they forget to release somewhere in their code.

Then of course, you want to access image pixels, and that’s where it gets tough.  OpenCV does provide get and set functions, but the performance hits start to accumulate when building real-time systems.

In this article, with the help of boost::shared_ptr, I design blImage, a simple, very fast, secure, yet scalable image structure, which I will use for all my work.

Here’s some sample code showing the potential of what will be possible with blImage:

int main(int argc, char *argv[])
{
    // Create a blank 8-bit color image
    blImage< blColor3<unsigned char> > MyImage;

    // Load an image from file
    MyImage.LoadImageFromFile("C:\\tux.png");

    // Create a floating point grayscale
    // version of our colored image
    blImage<unsigned char> MyImageFloatGray = MyImage;

    // Create two windows and show
    // both images side by side
    cvNamedWindow("ColorImage",CV_WINDOW_AUTOSIZE);
    cvNamedWindow("GrayscaleImage",CV_WINDOW_AUTOSIZE);
    cvShowImage("ColorImage",MyImage);
    cvShowImage("GrayscaleImage",MyImageFloatGray);

    // wait for a key
    cvWaitKey(0);
    return 0;
}

This is what we get when we run the above snippet

Dependencies

  1. boost::shared_ptr from the boost c++ libraries
    1. boost::weak_ptr could be used instead if you like
  2. IplImage from the OpenCV library (obviously)
  3. CvScalar from the OpenCV library (for loading images into blImage)

Design Philosophy

Accessibility is the mother of all catalysts when it comes to invention. The more people have access to more tools, the faster we move forward in a never ending journey of advancing technology. But, as the world would have it, easy access to technology can lead to a chaotic soup of unsustainable and unstable systems.

Thus, our image structure abides by the following principles:

  1. Simplicity
    1. Easy to read/understand code (a kid should be able to use it)
    2. Follows a well documented layout
  2. Scalability
    1. Easy to derive from to add functionality
    2. Could be used for small or large programs
    3. Could be used as a generalized Matrix structure
  3. Performance
    1. Incur as little cost as possible compared to a raw IplImage* pointer
  4. Robustness
    1. Account for possible errors and provide feedback

Image Depth

Before we build our image structure, we have to account for the different depths that we could need. For example, we might be playing with an 8-bit image in one algorithm, and a 32-bit image in another algorithm, or be mixing and matching images of different depths.

In this design, we account for the following depths:

  1. “bool” — (1-bit)
  2. “unsigned char” and “char” — (8-bits unsigned and signed)
  3. “unsigned short” and “short” — (16-bits unsigned and signed)
  4. “int” — (32-bits signed, we Only account for the 32-bit signed)
  5. “float” — (32-bits floating point)
  6. “double” — (64-bits floating point)

Note that for each depth, we have a range for the possible pixel values:

  1. 1-bit — [0,1]
  2. 8-bit unsigned — [0,255]
  3. 8-bit signed — [-128,127]
  4. 16-bit unsigned — [0,65535]
  5. 16-bit signed — [-32768,32767]
  6. 32-bit signed — [-2147483648,2147483647]
  7. 32-bit float — [0,1]
  8. 64-bit double — [0,1]

Image Channels

Of course, we can’t have an image without having color. We represent color by introducing the idea of channels. OpenCV uses a BGR sequence as opposed to the more common RGB to store values. Also, since our image structure can be used as a generalized matrix, we need to account for several cases:

  1. One Channel — One dimensional color
    1. Grayscale images
    2. Single-valued matrices (Linear Algebra)
  2. Two Channels –Two dimensional color
    1. For special type images
    2. Complex valued matrices (Complex Algebra)
  3. Three Channels — Three dimensional color
    1. Colored images (BGR or HSV images)
  4. Four dimensional color
    1. Colored images with alpha transparency values (ex. BGRA)

Representation of Color

For One-Channel images we’re going to use c++ basic data types corresponding to the depth of the images.

For Two-Channel images we’re going to use std::complex<T> with T corresponding to the depth of the images.

For Three-Channel images, we’re going to build a basic three-color structure, remembering that IplImage uses a BGR sequence instead of the widespread RGB sequence. We’re calling this color structure blColor3, and saving it in a header file called blColor3.hpp

blColor3.hpp (Click to see code...)
#ifndef BL_COLOR3_HPP
#define BL_COLOR3_HPP

//-------------------------------------------------------------------
// FILE:            blColor3.hpp
// CLASS:           blColor3
// BASE CLASS:      None
//
// PURPOSE:         A simple and efficient color structure of three
//                  components saved in a Blue,Green,Red sequence
//
// AUTHOR:          Vincenzo Barbato
//                  http://www.barbatolabs.com
//                  navyenzo@gmail.com
//
// LISENSE:         MIT-LICENCE
//                  http://www.opensource.org/licenses/mit-license.php
//
// DEPENDENCIES:    CvScalar -- From opencv library
//
// NOTES:
//
// DATE CREATED:    May/31/2010
// DATE UPDATED:
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Includes and libs needed for this file and sub-files
//-------------------------------------------------------------------
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Enums used for this file and sub-files
//-------------------------------------------------------------------
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
class blColor3
{
public: // Constructors and destructors

    // Default constructor
    blColor3(const blDataType& DefaultValue = 0);

    // Constructor from three values
    blColor3(const blDataType& Blue,
             const blDataType& Green,
             const blDataType& Red);

    // Copy constructor
    template<typename blDataType2>
    blColor3(const blColor3<blDataType2>& Color);

    // Construction from a CvScalar
    blColor3(const CvScalar& OpenCVColor);

    // Destructor
    ~blColor3()
    {
    }

public: // Overloaded operators

    bool                            operator==(const blColor3<blDataType>& Color)const;
    bool                            operator!=(const blColor3<blDataType>& Color)const;

    const blColor3<blDataType>      operator-()const;
    blColor3<blDataType>&           operator+=(const blColor3<blDataType>& Color);
    blColor3<blDataType>&           operator-=(const blColor3<blDataType>& Color);
    blColor3<blDataType>&           operator*=(const blColor3<blDataType>& Color);
    blColor3<blDataType>&           operator/=(const blColor3<blDataType>& Color);
    blColor3<blDataType>&           operator%=(const blColor3<blDataType>& Color);
    blColor3<blDataType>&           operator*=(const blDataType& Scalar);
    blColor3<blDataType>&           operator/=(const blDataType& Scalar);
    blColor3<blDataType>&           operator%=(const blDataType& Scalar);

    const blColor3<blDataType>      operator+(const blColor3<blDataType>& Color)const;
    const blColor3<blDataType>      operator-(const blColor3<blDataType>& Color)const;
    const blColor3<blDataType>      operator*(const blDataType& Scalar)const;
    const blColor3<blDataType>      operator/(const blDataType& Scalar)const;
    const blColor3<blDataType>      operator%(const blDataType& Scalar)const;

    const blColor3<blDataType>      operator*(const blColor3<blDataType>& Color)const;
    const blColor3<blDataType>      operator/(const blColor3<blDataType>& Color)const;
    const blColor3<blDataType>      operator%(const blColor3<blDataType>& Color)const;

public: // Public functions

    // Functions used to convert
    // this color to a CvScalar
    operator CvScalar()
    {
        return CV_RGB(m_Red,m_Green,m_Blue);
    }
    operator const CvScalar()const
    {
        return CV_RGB(m_Red,m_Green,m_Blue);
    }

    // Functions used to convert
    // this color to a single value
    // NOTE:  The function uses the
    //        formula (0.30*R + 0.59*G + 0.11*B)
    operator blDataType()
    {
        return blDataType(0.11*m_Blue + 0.59*m_Green + 0.30*m_Red);
    }
    operator const blDataType()const
    {
        return blDataType(0.11*m_Blue + 0.59*m_Green + 0.30*m_Red);
    }

public: // Public variables

    // The color components
    blDataType                      m_Blue;
    blDataType                      m_Green;
    blDataType                      m_Red;
};
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor3<blDataType>::blColor3(const blDataType& DefaultValue)
{
    m_Blue = DefaultValue;
    m_Green = DefaultValue;
    m_Red = DefaultValue;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor3<blDataType>::blColor3(const blDataType& Blue,
                                      const blDataType& Green,
                                      const blDataType& Red)
{
    m_Blue = Blue;
    m_Green = Green;
    m_Red = Red;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
template<typename blDataType2>
inline blColor3<blDataType>::blColor3(const blColor3<blDataType2>& Color)
{
    m_Blue = blDataType(Color.m_Blue);
    m_Green = blDataType(Color.m_Green);
    m_Red = blDataType(Color.m_Red);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor3<blDataType>::blColor3(const CvScalar& OpenCVColor)
{
    m_Blue = OpenCVColor.val[0];
    m_Green = OpenCVColor.val[1];
    m_Red = OpenCVColor.val[2];
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline bool blColor3<blDataType>::operator==(const blColor3<blDataType>& Color)const
{
    if(Color.m_Blue == m_Blue &&
       Color.m_Green == m_Green &&
       Color.m_Red == m_Red)
        return true;
    else
        return false;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline bool blColor3<blDataType>::operator!=(const blColor3<blDataType>& Color)const
{
    if(Color.m_Blue == m_Blue &&
       Color.m_Green == m_Green &&
       Color.m_Red == m_Red)
        return false;
    else
        return true;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor3<blDataType> blColor3<blDataType>::operator-()const
{
    return blColor3<blDataType>(-m_Blue,-m_Green,-m_Red);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor3<blDataType>& blColor3<blDataType>::operator+=(const blColor3<blDataType>& Color)
{
    m_Blue += Color.m_Blue;
    m_Green += Color.m_Green;
    m_Red += Color.m_Red;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor3<blDataType>& blColor3<blDataType>::operator-=(const blColor3<blDataType>& Color)
{
    m_Blue -= Color.m_Blue;
    m_Green -= Color.m_Green;
    m_Red -= Color.m_Red;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor3<blDataType>& blColor3<blDataType>::operator*=(const blColor3<blDataType>& Color)
{
    m_Blue *= Color.m_Blue;
    m_Green *= Color.m_Green;
    m_Red *= Color.m_Red;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor3<blDataType>& blColor3<blDataType>::operator/=(const blColor3<blDataType>& Color)
{
    m_Blue /= Color.m_Blue;
    m_Green /= Color.m_Green;
    m_Red /= Color.m_Red;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor3<blDataType>& blColor3<blDataType>::operator%=(const blColor3<blDataType>& Color)
{
    m_Blue %= Color.m_Blue;
    m_Green %= Color.m_Green;
    m_Red %= Color.m_Red;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor3<blDataType>& blColor3<blDataType>::operator*=(const blDataType& Scalar)
{
    m_Blue *= Scalar;
    m_Green *= Scalar;
    m_Red *= Scalar;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor3<blDataType>& blColor3<blDataType>::operator/=(const blDataType& Scalar)
{
    m_Blue /= Scalar;
    m_Green /= Scalar;
    m_Red /= Scalar;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor3<blDataType>& blColor3<blDataType>::operator%=(const blDataType& Scalar)
{
    m_Blue %= Scalar;
    m_Green %= Scalar;
    m_Red %= Scalar;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor3<blDataType> blColor3<blDataType>::operator+(const blColor3<blDataType>& Color)const
{
    return blColor3<blDataType>(m_Blue + Color.m_Blue,
                                m_Green + Color.m_Green,
                                m_Red + Color.m_Red);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor3<blDataType> blColor3<blDataType>::operator-(const blColor3<blDataType>& Color)const
{
    return blColor3<blDataType>(m_Blue - Color.m_Blue,
                                m_Green - Color.m_Green,
                                m_Red - Color.m_Red);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor3<blDataType> blColor3<blDataType>::operator*(const blColor3<blDataType>& Color)const
{
    return blColor3<blDataType>(m_Blue * Color.m_Blue,
                                m_Green * Color.m_Green,
                                m_Red * Color.m_Red);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor3<blDataType> blColor3<blDataType>::operator/(const blColor3<blDataType>& Color)const
{
    return blColor3<blDataType>(m_Blue / Color.m_Blue,
                                m_Green / Color.m_Green,
                                m_Red / Color.m_Red);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor3<blDataType> blColor3<blDataType>::operator%(const blColor3<blDataType>& Color)const
{
    return blColor3<blDataType>(m_Blue % Color.m_Blue,
                                m_Green % Color.m_Green,
                                m_Red % Color.m_Red);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor3<blDataType> blColor3<blDataType>::operator*(const blDataType& Scalar)const
{
    return blColor3<blDataType>(m_Blue * Scalar,
                                m_Green * Scalar,
                                m_Red * Scalar);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor3<blDataType> blColor3<blDataType>::operator/(const blDataType& Scalar)const
{
    return blColor3<blDataType>(m_Blue / Scalar,
                                m_Green / Scalar,
                                m_Red / Scalar);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor3<blDataType> blColor3<blDataType>::operator%(const blDataType& Scalar)const
{
    return blColor3<blDataType>(m_Blue % Scalar,
                                m_Green % Scalar,
                                m_Red % Scalar);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor3<blDataType> operator*(const blDataType& Scalar,
                                      const blColor3<blDataType>& Color)
{
    return blColor3<blDataType>(Color.m_Blue * Scalar,
                                Color.m_Green * Scalar,
                                Color.m_Red * Scalar);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor3<blDataType> operator/(const blDataType& Scalar,
                                      const blColor3<blDataType>& Color)
{
    return blColor3<blDataType>(Scalar / Color.m_Blue,
                                Scalar / Color.m_Green,
                                Scalar / Color.m_Red);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor3<blDataType> operator%(const blDataType& Scalar,
                                      const blColor3<blDataType>& Color)
{
    return blColor3<blDataType>(Scalar % Color.m_Blue,
                                Scalar % Color.m_Green,
                                Scalar % Color.m_Red);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline std::ostream& operator<<(std::ostream& os,const blColor3<blDataType>& Color)
{
    os << Color.m_Blue << " ";
    os << Color.m_Green << " ";
    os << Color.m_Red;
    return os;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline std::istream& operator>>(std::istream& is,blColor3<blDataType>& Color)
{
    is >> Color.m_Blue;
    is >> Color.m_Green;
    is >> Color.m_Red;

    return is;
}
//-------------------------------------------------------------------

#endif // BL_COLOR3_HPP

For Four-Channel images, we’re going to derive from the three-color structure, and add an Alpha channel

blColor4.hpp (Click to see code...)
#ifndef BL_COLOR4_HPP
#define BL_COLOR4_HPP

//-------------------------------------------------------------------
// FILE:            blColor4.hpp
// CLASS:           blColor4
// BASE CLASS:      blColor3
//
// PURPOSE:         Based on blColor3, adds an alpha transparency value
//
// AUTHOR:          Vincenzo Barbato
//                  http://www.barbatolabs.com
//                  navyenzo@gmail.com
//
// LISENSE:         MIT-LICENCE
//                  http://www.opensource.org/licenses/mit-license.php
//
// DEPENDENCIES:    blColor4
//
// NOTES:
//
// DATE CREATED:    May/31/2010
// DATE UPDATED:
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Includes and libs needed for this file and sub-files
//-------------------------------------------------------------------
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Enums used for this file and sub-files
//-------------------------------------------------------------------
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
class blColor4 : public blColor3<blDataType>
{
public: // Constructors and destructors

    // Default constructor
    blColor4(const blDataType& DefaultValue = 0);

    // Constructor from four values
    blColor4(const blDataType& Blue,
             const blDataType& Green,
             const blDataType& Red,
             const blDataType& Alpha);

    // Copy constructor
    template<typename blDataType2>
    blColor4(const blColor4<blDataType2>& Color);

    // Construction from a CvScalar
    blColor4(const CvScalar& OpenCVColor);

    // Destructor
    ~blColor4()
    {
    }

public: // Overloaded operators

    bool                                operator==(const blColor4<blDataType>& Color)const;
    bool                                operator!=(const blColor4<blDataType>& Color)const;

    const blColor4<blDataType>          operator-()const;
    blColor4<blDataType>&               operator+=(const blColor4<blDataType>& Color);
    blColor4<blDataType>&               operator-=(const blColor4<blDataType>& Color);
    blColor4<blDataType>&               operator*=(const blColor4<blDataType>& Color);
    blColor4<blDataType>&               operator/=(const blColor4<blDataType>& Color);
    blColor4<blDataType>&               operator%=(const blColor4<blDataType>& Color);
    blColor4<blDataType>&               operator*=(const blDataType& Scalar);
    blColor4<blDataType>&               operator/=(const blDataType& Scalar);
    blColor4<blDataType>&               operator%=(const blDataType& Scalar);

    const blColor4<blDataType>          operator+(const blColor4<blDataType>& Color)const;
    const blColor4<blDataType>          operator-(const blColor4<blDataType>& Color)const;
    const blColor4<blDataType>          operator*(const blDataType& Scalar)const;
    const blColor4<blDataType>          operator/(const blDataType& Scalar)const;
    const blColor4<blDataType>          operator%(const blDataType& Scalar)const;

    const blColor4<blDataType>          operator*(const blColor4<blDataType>& Color)const;
    const blColor4<blDataType>          operator/(const blColor4<blDataType>& Color)const;
    const blColor4<blDataType>          operator%(const blColor4<blDataType>& Color)const;

public: // Public functions

    // Functions used to convert this color
    // to a CvScalar
    operator CvScalar()
    {
        CvScalar Color;
        Color.val[0] = this->m_Blue;
        Color.val[1] = this->m_Green;
        Color.val[2] = this->m_Red;
        Color.val[3] = m_Alpha;
        return Color;
    }
    operator const CvScalar()const
    {
        CvScalar Color;
        Color.val[0] = this->m_Blue;
        Color.val[1] = this->m_Green;
        Color.val[2] = this->m_Red;
        Color.val[3] = m_Alpha;
        return Color;
    }

public: // Public variables

    // The alpha component
    blDataType                          m_Alpha;
};
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor4<blDataType>::blColor4(const blDataType& DefaultValue)
                                      : blColor3<blDataType>(DefaultValue)
{
    m_Alpha = 1;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor4<blDataType>::blColor4(const blDataType& Blue,
                                      const blDataType& Green,
                                      const blDataType& Red,
                                      const blDataType& Alpha)
                                      : blColor3<blDataType>(Blue,Green,Red)
{
    m_Alpha = Alpha;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
template<typename blDataType2>
inline blColor4<blDataType>::blColor4(const blColor4<blDataType2>& Color)
                                      : blColor3<blDataType>(Color)
{
    m_Alpha = Color.m_Alpha;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor4<blDataType>::blColor4(const CvScalar& OpenCVColor)
                                      : blColor3<blDataType>(OpenCVColor)
{
    m_Alpha = OpenCVColor.val[3];
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline bool blColor4<blDataType>::operator==(const blColor4<blDataType>& Color)const
{
    if(Color.m_Blue == this->m_Blue &&
       Color.m_Green == this->m_Green &&
       Color.m_Red == this->m_Red &&
       Color.m_Alpha == m_Alpha)
        return true;
    else
        return false;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline bool blColor4<blDataType>::operator!=(const blColor4<blDataType>& Color)const
{
    if(Color.m_Blue == this->m_Blue &&
       Color.m_Green == this->m_Green &&
       Color.m_Red == this->m_Red &&
       Color.m_Alpha == m_Alpha)
        return false;
    else
        return true;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor4<blDataType> blColor4<blDataType>::operator-()const
{
    return blColor4<blDataType>(-this->m_Blue,
                            -this->m_Green,
                            -this->m_Red,
                            -this->m_Alpha);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor4<blDataType>& blColor4<blDataType>::operator+=(const blColor4<blDataType>& Color)
{
    this->m_Blue += Color.m_Blue;
    this->m_Green += Color.m_Green;
    this->m_Red += Color.m_Red;
    m_Alpha += Color.m_Alpha;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor4<blDataType>& blColor4<blDataType>::operator-=(const blColor4<blDataType>& Color)
{
    this->m_Blue -= Color.m_Blue;
    this->m_Green -= Color.m_Green;
    this->m_Red -= Color.m_Red;
    m_Alpha -= Color.m_Alpha;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor4<blDataType>& blColor4<blDataType>::operator*=(const blColor4<blDataType>& Color)
{
    this->m_Blue *= Color.m_Blue;
    this->m_Green *= Color.m_Green;
    this->m_Red *= Color.m_Red;
    m_Alpha *= Color.m_Alpha;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor4<blDataType>& blColor4<blDataType>::operator/=(const blColor4<blDataType>& Color)
{
    this->m_Blue /= Color.m_Blue;
    this->m_Green /= Color.m_Green;
    this->m_Red /= Color.m_Red;
    m_Alpha /= Color.m_Alpha;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor4<blDataType>& blColor4<blDataType>::operator%=(const blColor4<blDataType>& Color)
{
    this->m_Blue %= Color.m_Blue;
    this->m_Green %= Color.m_Green;
    this->m_Red %= Color.m_Red;
    m_Alpha %= Color.m_Alpha;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor4<blDataType>& blColor4<blDataType>::operator*=(const blDataType& Scalar)
{
    this->m_Blue *= Scalar;
    this->m_Green *= Scalar;
    this->m_Red *= Scalar;
    m_Alpha *= Scalar;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor4<blDataType>& blColor4<blDataType>::operator/=(const blDataType& Scalar)
{
    this->m_Blue /= Scalar;
    this->m_Green /= Scalar;
    this->m_Red /= Scalar;
    m_Alpha /= Scalar;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor4<blDataType>& blColor4<blDataType>::operator%=(const blDataType& Scalar)
{
    this->m_Blue %= Scalar;
    this->m_Green %= Scalar;
    this->m_Red %= Scalar;
    m_Alpha %= Scalar;

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor4<blDataType> blColor4<blDataType>::operator+(const blColor4<blDataType>& Color)const
{
    return blColor4<blDataType>(this->m_Blue + Color.m_Blue,
                                this->m_Green + Color.m_Green,
                                this->m_Red + Color.m_Red,
                                m_Alpha + Color.m_Alpha);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor4<blDataType> blColor4<blDataType>::operator-(const blColor4<blDataType>& Color)const
{
    return blColor4<blDataType>(this->m_Blue - Color.m_Blue,
                                this->m_Green - Color.m_Green,
                                this->m_Red - Color.m_Red,
                                m_Alpha - Color.m_Alpha);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor4<blDataType> blColor4<blDataType>::operator*(const blColor4<blDataType>& Color)const
{
    return blColor4<blDataType>(this->m_Blue * Color.m_Blue,
                                this->m_Green * Color.m_Green,
                                this->m_Red * Color.m_Red,
                                m_Alpha * Color.m_Alpha);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor4<blDataType> blColor4<blDataType>::operator/(const blColor4<blDataType>& Color)const
{
    return blColor4<blDataType>(this->m_Blue / Color.m_Blue,
                                this->m_Green / Color.m_Green,
                                this->m_Red / Color.m_Red,
                                m_Alpha / Color.m_Alpha);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor4<blDataType> blColor4<blDataType>::operator%(const blColor4<blDataType>& Color)const
{
    return blColor4<blDataType>(this->m_Blue % Color.m_Blue,
                                this->m_Green % Color.m_Green,
                                this->m_Red % Color.m_Red,
                                m_Alpha % Color.m_Alpha);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor4<blDataType> blColor4<blDataType>::operator*(const blDataType& Scalar)const
{
    return blColor4<blDataType>(this->m_Blue * Scalar,
                                this->m_Green * Scalar,
                                this->m_Red * Scalar,
                                m_Alpha * Scalar);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor4<blDataType> blColor4<blDataType>::operator/(const blDataType& Scalar)const
{
    return blColor4<blDataType>(this->m_Blue / Scalar,
                                this->m_Green / Scalar,
                                this->m_Red / Scalar,
                                m_Alpha / Scalar);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline const blColor4<blDataType> blColor4<blDataType>::operator%(const blDataType& Scalar)const
{
    return blColor4<blDataType>(this->m_Blue % Scalar,
                                this->m_Green % Scalar,
                                this->m_Red % Scalar,
                                m_Alpha % Scalar);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor4<blDataType> operator*(const blDataType& Scalar,
                                  const blColor4<blDataType>& Color)
{
    return blColor4<blDataType>(Color.m_Blue * Scalar,
                                Color.m_Green * Scalar,
                                Color.m_Red * Scalar,
                                Color.m_Alpha * Scalar);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor4<blDataType> operator/(const blDataType& Scalar,
                                      const blColor4<blDataType>& Color)
{
    return blColor4<blDataType>(Scalar / Color.m_Blue,
                                Scalar / Color.m_Green,
                                Scalar / Color.m_Red,
                                Scalar / Color.m_Alpha);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline blColor4<blDataType> operator%(const blDataType& Scalar,
                                      const blColor4<blDataType>& Color)
{
    return blColor4<blDataType>(Scalar % Color.m_Blue,
                                Scalar % Color.m_Green,
                                Scalar % Color.m_Red,
                                Scalar % Color.m_Alpha);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline std::ostream& operator<<(std::ostream& os,const blColor4<blDataType>& Color)
{
    os << Color.m_Blue << " ";
    os << Color.m_Green << " ";
    os << Color.m_Red << " ";
    os << Color.m_Alpha;
    return os;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blDataType>
inline std::istream& operator>>(std::istream& is,blColor4<blDataType>& Color)
{
    is >> Color.m_Blue;
    is >> Color.m_Green;
    is >> Color.m_Red;
    is >> Color.m_Alpha;

    return is;
}
//-------------------------------------------------------------------

#endif // BL_COLOR4_HPP

Clean Up Resources Functor

Since we’re going to use boost::shared_ptr to hold our images, we’re going to need a simple functor for shared_ptr to properly release our images after we’re done using them. We’re going to put the functor in a separate file called blCleanResources.hpp, which will serve as a depository for other clean up code.

blCleanResources.hpp (Click to see code...)
#ifndef BL_CLEANRESOURCES_HPP
#define BL_CLEANRESOURCES_HPP

//-------------------------------------------------------------------
// FILE:            blCleanResources.hpp
// CLASS:           None
// BASE CLASS:      None
// PURPOSE:         Special functions needed to handle the release
//                  of various resources such as OpenCV's IplImage
// AUTHOR:          Vincenzo Barbato
//                  http://www.barbatolabs.com
//                  navyenzo@gmail.com
// LISENSE:         MIT-LICENCE
//                  http://www.opensource.org/licenses/mit-license.php
// DEPENDENCIES:    IplImage -- Image structure from opencv
//                  cvReleaseImage -- To release the image
//                  CvCapture -- Capture device structure from opencv
//                  cvReleaseCapture -- To release the capture device
//                  CvMemStorage -- Memory storage structure from opencv
//                  cvReleaseMemStorage -- To realase the memory storage
// NOTES:
// DATE CREATED:    May/30/2010
// DATE UPDATED:
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Includes and libs needed for this file and sub-files
//-------------------------------------------------------------------
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Enums used for this file and sub-files
//-------------------------------------------------------------------
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Functor used to delete a shared_ptr without
// destroying the objects it's pointing to
//-------------------------------------------------------------------
class null_deleter
{
public:

    // Overloaded operator used to release an IplImage
    void operator()(void const*)const
    {
    }
};
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Functor used to release an IplImage
//-------------------------------------------------------------------
class ReleaseImage
{
public:

    // Overloaded operator used to release an IplImage
    void operator()(IplImage* Image)
    {
        // Check if we have an image
        if(Image == NULL)
            return;

        // Release the image
        cvReleaseImage(&Image);

        // Nullify the pointer
        Image = NULL;
    }
};
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Functor used to release an OpenCV Capture Device
//-------------------------------------------------------------------
class ReleaseCaptureDevice
{
public:

    // Overloaded operator used to
    // release a capture device
    void operator()(CvCapture* CaptureDevice)
    {
        // Check if we have a capture device
        if(CaptureDevice == NULL)
            return;

        // Release the image
        cvReleaseCapture(&CaptureDevice);

        // Nullify the pointer
        CaptureDevice = NULL;
    }
};
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Functor used to release an OpenCV memory storage
//-------------------------------------------------------------------
class ReleaseMemStorage
{
public:

    // Overloaded operator used to
    // release a memory storage
    void operator()(CvMemStorage* MemoryStorage)
    {
        // Check if we have a capture device
        if(MemoryStorage == NULL)
            return;

        // Release the image
        cvReleaseMemStorage(&MemoryStorage);

        // Nullify the pointer
        MemoryStorage = NULL;
    }
};
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Functor used to release an OpenCV file storage
//-------------------------------------------------------------------
class ReleaseFileStorage
{
public:

    // Overloaded operator used to
    // release a memory storage
    void operator()(CvFileStorage* FileStorage)
    {
        // Check if we have a capture device
        if(FileStorage == NULL)
            return;

        // Release the image
        cvReleaseFileStorage(&FileStorage);

        // Nullify the pointer
        FileStorage = NULL;
    }
};
//-------------------------------------------------------------------

#endif // BL_CLEANRESOURCES_HPP

Our Image Structure

Finally, we have come to our basic image structure. We’re going to separate the image structure into two classes:

  1. blImg — (Base class used to wrap raw IplImage pointers)
    1. Automatic garbage collection (with the use of boost::shared_ptr)
    2. Generic image coding (through the use of template meta-programming)
    3. Direct, efficient and easy access to pixels.
  2. blImage — (Derived class used to add basic image functionality)
    1. Provides constructors and functions to build, load and copy images from several sources, such as image files, images in memory, images from webcams, generalized matrix data and

The base blImg class is defined in a header file called blImg.hpp. This is a very long file that contains all the template specialization code to handle different image depths and number of channels

blImg.hpp (Click to see code...)
#ifndef BL_IMG_HPP
#define BL_IMG_HPP

//-------------------------------------------------------------------
// FILE:            blImg.hpp
// CLASS:           blImg
// BASE CLASS:      None
//
// PURPOSE:         A base class used to wrap OpenCV's image
//                  class IplImage with a boost::shared_ptr
//
// AUTHOR:          Vincenzo Barbato
//                  http://www.barbatolabs.com
//                  navyenzo@gmail.com
//
// LISENSE:         MIT-LICENCE
//                  http://www.opensource.org/licenses/mit-license.php
//
// DEPENDENCIES:    IplImage -- From opencv library
//                  boost::shared_ptr -- From boost c++ libraries
//                  IPL_DEPTH_1U -- For bool images
//                  IPL_DEPTH_8U -- For unsigned char images
//                  IPL_DEPTH_8S -- For char images
//                  IPL_DEPTH_16U -- For unsigned short images
//                  IPL_DEPTH_16S -- For short images
//                  IPL_DEPTH_32S -- For int images
//                  IPL_DEPTH_32F -- For float images
//                  IPL_DEPTH_64F -- For double images
//
// NOTES:
//
// DATE CREATED:    Jun/03/2010
// DATE UPDATED:
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Includes and libs needed for this file
//-------------------------------------------------------------------
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Enums needed for this file
//-------------------------------------------------------------------
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
class blImg
{
public: // Constructors and destructors

    // Default constructor
    blImg(const int& NumOfRows = 1,const int& NumOfCols = 1);

    // Copy constructor
    blImg(const blImg<blType>& Image);

    // Destructor
    ~blImg()
    {
    }

public: // Assignment operator

    // Overloaded assignment operator
    blImg<blType>&      operator=(const blImg<blType>& Image);

public: // Public functions

    // Overloaded [] operator (For efficient and easy image access)
    blType*             operator[](const int& RowIndex);
    const blType*       operator[](const int& RowIndex)const;

    // Function used to create an empty image
    void                CreateImage(const int& NumOfRows,
                                    const int& NumOfCols);

    // Function used to convert the scale of the image
    void                ConvertScale(const IplImage* Src,IplImage* Dst);

    // Function used to convert this object to a pure OpenCV IplImage
    operator IplImage* const()
    {
        if(m_Image.use_count() > 0)
            return m_Image.get();
        else
            return NULL;
    }

    // Function used to convert this const object to a pure OpenCV IplImage
    operator const IplImage* const()const
    {
        if(m_Image.use_count() > 0)
            return m_Image.get();
        else
            return NULL;
    }

    // Function used to get the depth of the image
    const int           GetDepth()const;

    // Function used to get the number of channels of the image
    const int           GetNumOfChannels()const;

    // Function used to get raw image data pointer
    const char*         GetImageData()const;

    // Function used to get raw image
    // data pointer cast to the data
    // type corresponding to this image
    const blType*       GetImageDataCast()const;

    // Functions used to get the min/max values representable in the image
    const int           GetRangeMin()const;
    const int           GetRangeMax()const;

    // Functions used to get the sizes of the image
    const int&          size1()const;
    const int&          size2()const;

    // Function used to get the
    // size of the image in bytes
    const int&          GetSizeInBytes()const;

    // Function used to get
    // the width step
    const int&          GetWidthStep()const;

    // Funtion used to get
    // whether the image origin
    // is top-left (0) or
    // bottom-left (1)
    const int&          GetOrigin()const;

    // Function used to get
    // the data order, whether
    // the channels are interleaved (0)
    // or separate (1)
    const int&          GetDataOrder()const;

protected: // Protected variables

    // Image variable
    boost::shared_ptr<IplImage>     m_Image;
};
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline blImg<blType>::blImg(const int& NumOfRows,const int& NumOfCols)
{
    // Create an empty image to the correct size
    CreateImage(NumOfRows,NumOfCols);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline blImg<blType>::blImg(const blImg<blType>& Image)
{
    // Clone the image and store
    // it inside the shared pointer
    this->m_Image = shared_ptr<IplImage>(cvCloneImage(Image),ReleaseImage());
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline void blImg<blType>::CreateImage(const int& NumOfRows,
                                       const int& NumOfCols)
{
    // Create the empty image
    IplImage* Img = NULL;
    Img = cvCreateImage(cvSize(NumOfCols,NumOfRows),
                        this->GetDepth(),
                        this->GetNumOfChannels());

    // Check if the image was created successfully
    if(Img != NULL)
    {
        m_Image = boost::shared_ptr<IplImage>(Img,ReleaseImage());
    }
    else
    {
        // Error -- We failed to
        //          create an image
    }

    return;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline blImg<blType>& blImg<blType>::operator=(const blImg<blType>& Image)
{
    // Clone the image and store
    // it inside the shared pointer
    this->m_Image = shared_ptr<IplImage>(cvCloneImage(Image),ReleaseImage());

    return (*this);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline blType* blImg<blType>::operator[](const int& RowIndex)
{
    return reinterpret_cast<blType*>(m_Image->imageData + m_Image->widthStep*RowIndex);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline const blType* blImg<blType>::operator[](const int& RowIndex)const
{
    return reinterpret_cast<blType*>(m_Image->imageData + m_Image->widthStep*RowIndex);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline const int& blImg<blType>::size1()const
{
    return m_Image->height;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline const int& blImg<blType>::size2()const
{
    return m_Image->width;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline const int& blImg<blType>::GetSizeInBytes()const
{
    return m_Image->imageSize;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline const int& blImg<blType>::GetWidthStep()const
{
    return m_Image->widthStep;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline const int& blImg<blType>::GetOrigin()const
{
    return m_Image->origin;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline const int& blImg<blType>::GetDataOrder()const
{
    return m_Image->dataOrder;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline const char* blImg<blType>::GetImageData()const
{
    if(m_Image.use_count() > 0)
        return m_Image->imageData;
    else
    {
        // Error
        return NULL;
    }
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline const blType* blImg<blType>::GetImageDataCast()const
{
    if(m_Image.use_count() > 0)
        return (blType*)(m_Image->imageData);
    else
    {
        // Error
        return NULL;
    }
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Template specializations of the function GetDepth used to
// get the OpenCV depth value for this image
//-------------------------------------------------------------------
template<typename blType>
inline const int blImg<blType>::GetDepth()const{return IPL_DEPTH_8U;}

template<>
inline const int blImg<bool>::GetDepth()const{return IPL_DEPTH_1U;}

template<>
inline const int blImg<unsigned char>::GetDepth()const{return IPL_DEPTH_8U;}

template<>
inline const int blImg<char>::GetDepth()const{return IPL_DEPTH_8S;}

template<>
inline const int blImg<unsigned short>::GetDepth()const{return IPL_DEPTH_16U;}

template<>
inline const int blImg<short>::GetDepth()const{return IPL_DEPTH_16S;}

template<>
inline const int blImg<int>::GetDepth()const{return IPL_DEPTH_32S;}

template<>
inline const int blImg<float>::GetDepth()const{return IPL_DEPTH_32F;}

template<>
inline const int blImg<double>::GetDepth()const{return IPL_DEPTH_64F;}

template<>
inline const int blImg< std::complex<bool> >::GetDepth()const{return IPL_DEPTH_1U;}

template<>
inline const int blImg< std::complex<unsigned char> >::GetDepth()const{return IPL_DEPTH_8U;}

template<>
inline const int blImg< std::complex<char> >::GetDepth()const{return IPL_DEPTH_8S;}

template<>
inline const int blImg< std::complex<unsigned short> >::GetDepth()const{return IPL_DEPTH_16U;}

template<>
inline const int blImg< std::complex<short> >::GetDepth()const{return IPL_DEPTH_16S;}

template<>
inline const int blImg< std::complex<int> >::GetDepth()const{return IPL_DEPTH_32S;}

template<>
inline const int blImg< std::complex<float> >::GetDepth()const{return IPL_DEPTH_32F;}

template<>
inline const int blImg< std::complex<double> >::GetDepth()const{return IPL_DEPTH_64F;}

template<>
inline const int blImg< blColor3<bool> >::GetDepth()const{return IPL_DEPTH_1U;}

template<>
inline const int blImg< blColor3<unsigned char> >::GetDepth()const{return IPL_DEPTH_8U;}

template<>
inline const int blImg< blColor3<char> >::GetDepth()const{return IPL_DEPTH_8S;}

template<>
inline const int blImg< blColor3<unsigned short> >::GetDepth()const{return IPL_DEPTH_16U;}

template<>
inline const int blImg< blColor3<short> >::GetDepth()const{return IPL_DEPTH_16S;}

template<>
inline const int blImg< blColor3<int> >::GetDepth()const{return IPL_DEPTH_32S;}

template<>
inline const int blImg< blColor3<float> >::GetDepth()const{return IPL_DEPTH_32F;}

template<>
inline const int blImg< blColor3<double> >::GetDepth()const{return IPL_DEPTH_64F;}

template<>
inline const int blImg< blColor4<bool> >::GetDepth()const{return IPL_DEPTH_1U;}

template<>
inline const int blImg< blColor4<unsigned char> >::GetDepth()const{return IPL_DEPTH_8U;}

template<>
inline const int blImg< blColor4<char> >::GetDepth()const{return IPL_DEPTH_8S;}

template<>
inline const int blImg< blColor4<unsigned short> >::GetDepth()const{return IPL_DEPTH_16U;}

template<>
inline const int blImg< blColor4<short> >::GetDepth()const{return IPL_DEPTH_16S;}

template<>
inline const int blImg< blColor4<int> >::GetDepth()const{return IPL_DEPTH_32S;}

template<>
inline const int blImg< blColor4<float> >::GetDepth()const{return IPL_DEPTH_32F;}

template<>
inline const int blImg< blColor4<double> >::GetDepth()const{return IPL_DEPTH_64F;}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Template specializations of the function GetNumOfChannels used to
// get the number of channels of an IplImage
//-------------------------------------------------------------------
template<typename blType>
inline const int blImg<blType>::GetNumOfChannels()const{return 1;}

template<>
inline const int blImg< std::complex<bool> >::GetNumOfChannels()const{return 2;}

template<>
inline const int blImg< std::complex<unsigned char> >::GetNumOfChannels()const{return 2;}

template<>
inline const int blImg< std::complex<char> >::GetNumOfChannels()const{return 2;}

template<>
inline const int blImg< std::complex<unsigned short> >::GetNumOfChannels()const{return 2;}

template<>
inline const int blImg< std::complex<short> >::GetNumOfChannels()const{return 2;}

template<>
inline const int blImg< std::complex<int> >::GetNumOfChannels()const{return 2;}

template<>
inline const int blImg< std::complex<float> >::GetNumOfChannels()const{return 2;}

template<>
inline const int blImg< std::complex<double> >::GetNumOfChannels()const{return 2;}

template<>
inline const int blImg< blColor3<bool> >::GetNumOfChannels()const{return 3;}

template<>
inline const int blImg< blColor3<unsigned char> >::GetNumOfChannels()const{return 3;}

template<>
inline const int blImg< blColor3<char> >::GetNumOfChannels()const{return 3;}

template<>
inline const int blImg< blColor3<unsigned short> >::GetNumOfChannels()const{return 3;}

template<>
inline const int blImg< blColor3<short> >::GetNumOfChannels()const{return 3;}

template<>
inline const int blImg< blColor3<int> >::GetNumOfChannels()const{return 3;}

template<>
inline const int blImg< blColor3<float> >::GetNumOfChannels()const{return 3;}

template<>
inline const int blImg< blColor3<double> >::GetNumOfChannels()const{return 3;}

template<>
inline const int blImg< blColor4<bool> >::GetNumOfChannels()const{return 4;}

template<>
inline const int blImg< blColor4<unsigned char> >::GetNumOfChannels()const{return 4;}

template<>
inline const int blImg< blColor4<char> >::GetNumOfChannels()const{return 4;}

template<>
inline const int blImg< blColor4<unsigned short> >::GetNumOfChannels()const{return 4;}

template<>
inline const int blImg< blColor4<short> >::GetNumOfChannels()const{return 4;}

template<>
inline const int blImg< blColor4<int> >::GetNumOfChannels()const{return 4;}

template<>
inline const int blImg< blColor4<float> >::GetNumOfChannels()const{return 4;}

template<>
inline const int blImg< blColor4<double> >::GetNumOfChannels()const{return 4;}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Template specializations of the function GetRangeMin
// used to get the minimum value of the image pixel representable
// by the image depth
//-------------------------------------------------------------------
template<typename blType>
inline const int blImg<blType>::GetRangeMin()const{return 0;}

template<>
inline const int blImg<bool>::GetRangeMin()const{return std::numeric_limits<bool>::min();}

template<>
inline const int blImg<unsigned char>::GetRangeMin()const{return std::numeric_limits<unsigned char>::min();}

template<>
inline const int blImg<char>::GetRangeMin()const{return std::numeric_limits<char>::min();}

template<>
inline const int blImg<unsigned short>::GetRangeMin()const{return std::numeric_limits<unsigned short>::min();}

template<>
inline const int blImg<short>::GetRangeMin()const{return std::numeric_limits<short>::min();}

template<>
inline const int blImg<int>::GetRangeMin()const{return std::numeric_limits<int>::min();}

template<>
inline const int blImg<float>::GetRangeMin()const{return 0;}

template<>
inline const int blImg<double>::GetRangeMin()const{return 0;}

template<>
inline const int blImg< std::complex<bool> >::GetRangeMin()const{return std::numeric_limits<bool>::min();}

template<>
inline const int blImg< std::complex<unsigned char> >::GetRangeMin()const{return std::numeric_limits<unsigned char>::min();}

template<>
inline const int blImg< std::complex<char> >::GetRangeMin()const{return std::numeric_limits<char>::min();}

template<>
inline const int blImg< std::complex<unsigned short> >::GetRangeMin()const{return std::numeric_limits<unsigned short>::min();}

template<>
inline const int blImg< std::complex<short> >::GetRangeMin()const{return std::numeric_limits<short>::min();}

template<>
inline const int blImg< std::complex<int> >::GetRangeMin()const{return std::numeric_limits<int>::min();}

template<>
inline const int blImg< std::complex<float> >::GetRangeMin()const{return 0;}

template<>
inline const int blImg< std::complex<double> >::GetRangeMin()const{return 0;}

template<>
inline const int blImg< blColor3<bool> >::GetRangeMin()const{return std::numeric_limits<bool>::min();}

template<>
inline const int blImg< blColor3<unsigned char> >::GetRangeMin()const{return std::numeric_limits<unsigned char>::min();}

template<>
inline const int blImg< blColor3<char> >::GetRangeMin()const{return std::numeric_limits<char>::min();}

template<>
inline const int blImg< blColor3<unsigned short> >::GetRangeMin()const{return std::numeric_limits<unsigned short>::min();}

template<>
inline const int blImg< blColor3<short> >::GetRangeMin()const{return std::numeric_limits<short>::min();}

template<>
inline const int blImg< blColor3<int> >::GetRangeMin()const{return std::numeric_limits<int>::min();}

template<>
inline const int blImg< blColor3<float> >::GetRangeMin()const{return 0;}

template<>
inline const int blImg< blColor3<double> >::GetRangeMin()const{return 0;}

template<>
inline const int blImg< blColor4<bool> >::GetRangeMin()const{return std::numeric_limits<bool>::min();}

template<>
inline const int blImg< blColor4<unsigned char> >::GetRangeMin()const{return std::numeric_limits<unsigned char>::min();}

template<>
inline const int blImg< blColor4<char> >::GetRangeMin()const{return std::numeric_limits<char>::min();}

template<>
inline const int blImg< blColor4<unsigned short> >::GetRangeMin()const{return std::numeric_limits<unsigned short>::min();}

template<>
inline const int blImg< blColor4<short> >::GetRangeMin()const{return std::numeric_limits<short>::min();}

template<>
inline const int blImg< blColor4<int> >::GetRangeMin()const{return std::numeric_limits<int>::min();}

template<>
inline const int blImg< blColor4<float> >::GetRangeMin()const{return 0;}

template<>
inline const int blImg< blColor4<double> >::GetRangeMin()const{return 0;}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Template specializations of the function GetRangeMax
// used to get the maximum value of the image pixel representable
// by the image depth
//-------------------------------------------------------------------
template<typename blType>
inline const int blImg<blType>::GetRangeMax()const{return 1;}

template<>
inline const int blImg<bool>::GetRangeMax()const{return std::numeric_limits<bool>::max();}

template<>
inline const int blImg<unsigned char>::GetRangeMax()const{return std::numeric_limits<unsigned char>::max();}

template<>
inline const int blImg<char>::GetRangeMax()const{return std::numeric_limits<char>::max();}

template<>
inline const int blImg<unsigned short>::GetRangeMax()const{return std::numeric_limits<unsigned short>::max();}

template<>
inline const int blImg<short>::GetRangeMax()const{return std::numeric_limits<short>::max();}

template<>
inline const int blImg<int>::GetRangeMax()const{return std::numeric_limits<int>::max();}

template<>
inline const int blImg<float>::GetRangeMax()const{return 1;}

template<>
inline const int blImg<double>::GetRangeMax()const{return 1;}

template<>
inline const int blImg< std::complex<bool> >::GetRangeMax()const{return std::numeric_limits<bool>::max();}

template<>
inline const int blImg< std::complex<unsigned char> >::GetRangeMax()const{return std::numeric_limits<unsigned char>::max();}

template<>
inline const int blImg< std::complex<char> >::GetRangeMax()const{return std::numeric_limits<char>::max();}

template<>
inline const int blImg< std::complex<unsigned short> >::GetRangeMax()const{return std::numeric_limits<unsigned short>::max();}

template<>
inline const int blImg< std::complex<short> >::GetRangeMax()const{return std::numeric_limits<short>::max();}

template<>
inline const int blImg< std::complex<int> >::GetRangeMax()const{return std::numeric_limits<int>::max();}

template<>
inline const int blImg< std::complex<float> >::GetRangeMax()const{return 1;}

template<>
inline const int blImg< std::complex<double> >::GetRangeMax()const{return 1;}

template<>
inline const int blImg< blColor3<bool> >::GetRangeMax()const{return std::numeric_limits<bool>::max();}

template<>
inline const int blImg< blColor3<unsigned char> >::GetRangeMax()const{return std::numeric_limits<unsigned char>::max();}

template<>
inline const int blImg< blColor3<char> >::GetRangeMax()const{return std::numeric_limits<char>::max();}

template<>
inline const int blImg< blColor3<unsigned short> >::GetRangeMax()const{return std::numeric_limits<unsigned short>::max();}

template<>
inline const int blImg< blColor3<short> >::GetRangeMax()const{return std::numeric_limits<short>::max();}

template<>
inline const int blImg< blColor3<int> >::GetRangeMax()const{return std::numeric_limits<int>::max();}

template<>
inline const int blImg< blColor3<float> >::GetRangeMax()const{return 1;}

template<>
inline const int blImg< blColor3<double> >::GetRangeMax()const{return 1;}

template<>
inline const int blImg< blColor4<bool> >::GetRangeMax()const{return std::numeric_limits<bool>::max();}

template<>
inline const int blImg< blColor4<unsigned char> >::GetRangeMax()const{return std::numeric_limits<unsigned char>::max();}

template<>
inline const int blImg< blColor4<char> >::GetRangeMax()const{return std::numeric_limits<char>::min();}

template<>
inline const int blImg< blColor4<unsigned short> >::GetRangeMax()const{return std::numeric_limits<unsigned short>::max();}

template<>
inline const int blImg< blColor4<short> >::GetRangeMax()const{return std::numeric_limits<short>::max();}

template<>
inline const int blImg< blColor4<int> >::GetRangeMax()const{return std::numeric_limits<int>::max();}

template<>
inline const int blImg< blColor4<float> >::GetRangeMax()const{return 1;}

template<>
inline const int blImg< blColor4<double> >::GetRangeMax()const{return 1;}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline void blImg<blType>::ConvertScale(const IplImage* Src,IplImage* Dst)
{
    double SrcMin = 0;
    double SrcMax = 1;
    double DstMin = 0;
    double DstMax = 1;

    // Determine the minimum and
    // maximum rapresentable numbers of
    // the source image
    switch(Src->depth)
    {
        case IPL_DEPTH_1U:
            SrcMin = 0;
            SrcMax = 1;
            break;
        case IPL_DEPTH_8U:
            SrcMin = numeric_limits<unsigned char>::min();
            SrcMax = numeric_limits<unsigned char>::max();
            break;
        case IPL_DEPTH_8S:
            SrcMin = numeric_limits<char>::min();
            SrcMax = numeric_limits<char>::max();
            break;
        case IPL_DEPTH_16U:
            SrcMin = numeric_limits<unsigned short>::min();
            SrcMax = numeric_limits<unsigned short>::max();
            break;
        case IPL_DEPTH_16S:
            SrcMin = numeric_limits<short>::min();
            SrcMax = numeric_limits<short>::max();
            break;
        case IPL_DEPTH_32S:
            SrcMin = numeric_limits<int>::min();
            SrcMax = numeric_limits<int>::max();
            break;
        case IPL_DEPTH_32F:
            SrcMin = 0;
            SrcMax = 1;
            break;
        case IPL_DEPTH_64F:
            SrcMin = 0;
            SrcMax = 1;
            break;
    }

    // Determine the minimum and
    // maximum rapresentable numbers of
    // the destination image
    switch(Dst->depth)
    {
        case IPL_DEPTH_1U:
            DstMin = 0;
            DstMax = 1;
            break;
        case IPL_DEPTH_8U:
            DstMin = 0;
            DstMax = 255;
            break;
        case IPL_DEPTH_8S:
            DstMin = numeric_limits<char>::min();
            DstMax = numeric_limits<char>::max();
            break;
        case IPL_DEPTH_16U:
            DstMin = numeric_limits<unsigned short>::min();
            DstMax = numeric_limits<unsigned short>::max();
            break;
        case IPL_DEPTH_16S:
            DstMin = numeric_limits<short>::min();
            DstMax = numeric_limits<short>::max();
            break;
        case IPL_DEPTH_32S:
            DstMin = numeric_limits<int>::min();
            DstMax = numeric_limits<int>::max();
            break;
        case IPL_DEPTH_32F:
            DstMin = 0;
            DstMax = 1;
            break;
        case IPL_DEPTH_64F:
            DstMin = 0;
            DstMax = 1;
            break;
    }

    // Calculate the Scale and shift values
    double Scale = (DstMax - DstMin)/(SrcMax - SrcMin);
    double Shift = DstMin - Scale*SrcMin;

    cvConvertScale(Src,Dst,Scale,Shift);
}
//-------------------------------------------------------------------

#endif // BL_IMG_HPP

The derived blImage class is defined in a header file called blImage.hpp

blImage.hpp (Click to see code...)

#ifndef BL_IMAGE_HPP
#define BL_IMAGE_HPP

//-------------------------------------------------------------------
// FILE:            blImage.hpp
// CLASS:           blImage
// BASE CLASS:      blImg
//
// PURPOSE:         Based on blImg, adds more functionality to the
//                  OpenCV IplImage wrap
//
// AUTHOR:          Vincenzo Barbato
//                  http://www.barbatolabs.com
//                  navyenzo@gmail.com
//
// LISENSE:         MIT-LICENCE
//                  http://www.opensource.org/licenses/mit-license.php
//
// DEPENDENCIES:    blImg
//                  CvScalar -- To covert between different type images
//
// NOTES:
// DATE CREATED:    Jun/03/2010
// DATE UPDATED:
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Includes and libs needed for this file
//-------------------------------------------------------------------
//-------------------------------------------------------------------

//-------------------------------------------------------------------
// Enums needed for this file
//-------------------------------------------------------------------
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
class blImage : public blImg<blType>
{
public: // Constructors and destructors

    // Default constructor
    blImage(const int& Rows = 1,const int& Cols = 1);

    // Copy constructor
    blImage(const blImage<blType>& Image);

    // Copy constructor from a different type image
    template<typename blType2>
    blImage(const blImage<blType2>& Image,const int& InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2 = 0);

    // Constructor from a raw IplImage*
    blImage(const IplImage* Img,const int& InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2 = 0);

    // Constructor from a raw CvMat*
    blImage(const CvMat* Img,const int& InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2 = 0);

    // Construct image from raw 2D array
    template<int NumOfRows,int NumOfCols>
    blImage(const blType (&MatrixArray)[NumOfRows][NumOfCols]);

    // Destructor
    ~blImage()
    {
    }

public: // Public functions

    // Function used to wrap an existing
    // IplImage with a blImage and NULLIFY
    // the original IplImage pointer
    void            WrapIplImage(IplImage*& Img,
                                 const int& InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2 = 0);

    // Function used to load an IplImage into this object
    void            LoadImage(const IplImage* Img,
                              const int& InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2 = 0);

    // Function used to load a CvMat into this object
    void            LoadMatrix(const CvMat* Img,
                               const int& InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2 = 0);

    // Function used to load an image from file
    void            LoadImageFromFile(const string& Filename,
                                      const bool& ShouldImageBeFlipped = false,
                                      const int& ImageFlipMode = 0,
                                      const int& HowToReadImageColorAndDepth =
                                      CV_LOAD_IMAGE_ANYDEPTH |
                                      CV_LOAD_IMAGE_ANYCOLOR,
                                      const int& InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2 = 0);
};
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline blImage<blType>::blImage(const int& Rows,const int& Cols)
                                : blImg<blType>(Rows,Cols)
{
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline blImage<blType>::blImage(const blImage<blType>& Image) : blImg<blType>(Image)
{
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
template<typename blType2>
inline blImage<blType>::blImage(const blImage<blType2>& Image,
                                const int& InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2)
                                : blImg<blType>()
{
    // Since we have a different type image, we
    // have to do some extra work, but we do want
    // to copy image and not link
    LoadImage(Image,InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline blImage<blType>::blImage(const IplImage* Img,
                                const int& InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2)
                                : blImg<blType>()
{
    LoadImage(Img,InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline blImage<blType>::blImage(const CvMat* Img,
                                const int& InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2)
                                : blImg<blType>()
{
    this->LoadMatrix(Img,InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
template<int NumOfRows,int NumOfCols>
inline blImage<blType>::blImage(const blType (&MatrixArray)[NumOfRows][NumOfCols])
                                : blImg<blType>(NumOfRows,NumOfCols)
{
    // Fill the image values using the passed constant array
    for(int i = 0; i < NumOfRows; ++i)
        for(int j = 0; j < NumOfCols; ++j)
            (*this)[i][j] = MatrixArray[i][j];
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline void blImage<blType>::WrapIplImage(IplImage*& Img,
                                          const int& InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2)
{
    // First we have to check
    // if the passed image pointer
    // is NULL
    if(Img == NULL)
    {
        // Error -- Tried to wrap an
        //          IplImage pointer
        //          that was NULL
        return;
    }

    // Since blImage has a statically
    // determined depth and number of
    // channels, if we want to wrap
    // an existing IplImage pointer, we
    // have to check if both the depth
    // and number of channels match this
    // blImage
    if(Img->depth == this->GetDepth() &&
       Img->nChannels == this->GetNumOfChannels())
    {
        // In this case they match, therefore
        // we can simply link to the existing
        // image pointer and then NULLIFY it
        this->m_Image = boost::shared_ptr<IplImage>(Img,ReleaseImage());
        Img = NULL;
    }
    else
    {
        // In this case they don't match,
        // therefore we first copy the
        // existing image pointer to this
        // blImage and then release the original
        // image and NULLIFY the pointer
        this->LoadImage(Img,InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2);
        cvReleaseImage(&Img);
        Img = NULL;
    }
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline void blImage<blType>::LoadMatrix(const CvMat* Img,
                                        const int& InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2)
{
    // First we need to check
    // the validity of the
    // CvMat* pointer
    if(Img == NULL)
    {
        // Error -- Tried to load a
        //          CvMat* structure
        //          into this image
        //          using a NULL pointer.
        //          In this case we
        //          simply default
        //          this image to
        //          size 1x1 and return
        this->CreateImage(1,1);
        return;
    }

    // Now that we know we don't
    // have a NULL pointer, we
    // create an IplImage header
    // from the CvMat image
    IplImage MatToImg;
    cvGetImage(Img,&MatToImg);

    // Now we can finally load
    // the matrix into this image
    this->LoadImage(&MatToImg,InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2);
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline void blImage<blType>::LoadImage(const IplImage* Img,
                                       const int& InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2)
{
    // Because the image has some default depth and number of channels,
    // we have to check if it corresponds to this object's image depth and number
    // of channels, otherwise we have to do some extra work
    if(Img->depth == this->GetDepth() &&
       Img->nChannels == this->GetNumOfChannels())
    {
        this->m_Image = boost::shared_ptr<IplImage>(cvCloneImage(Img),ReleaseImage());
    }
    else
    {
        // In this case, the depths or channels don't match, so we
        // have to do some extra work.  We have to create a new image
        // with the correct depth and number of channels and then
        // copy the passed image into this one
        this->CreateImage(Img->height,Img->width);

        // Check for zero size
        if(Img->width == 0 || Img->height == 0)
            return;

        // If we created the image successfully then copy the images
        if(this->m_Image.use_count() > 0)
        {
            // Check the number of channels and call the appropriate functions
            if(this->GetNumOfChannels() == Img->nChannels)
            {
                // At this point we know that the number of channels match, but
                // that the depths are different, so we use the cvConvertScale
                // function to convert the image to the correct depth
                this->ConvertScale(Img,(*this));
            }
            else if(this->GetNumOfChannels() == 1 && Img->nChannels == 3)
            {
                if(this->GetDepth() == Img->depth)
                {
                    if(Img->depth == IPL_DEPTH_8U ||
                       Img->depth == IPL_DEPTH_16U ||
                       Img->depth == IPL_DEPTH_32F)
                    {
                        cvCvtColor(Img,(*this),CV_BGR2GRAY);
                    }
                    else
                    {
                        CvScalar Color;
                        for(int i = 0; i < this->size1(); ++i)
                        {
                            for(int j = 0; j < this->size2(); ++j)
                            {
                                Color = cvGet2D(Img,i,j);
                                (*this)[i][j] = 0.114*Color.val[0] + 0.587*Color.val[1] + 0.299*Color.val[2];
                            }
                        }
                    }
                }
                else
                {
                    // The depths are different so we have to create an intermediary
                    // image to take care of the two conversions

                    // Create a new temporary image from the loaded one
                    IplImage* Img2 = cvCreateImage(cvSize(Img->width,Img->height),Img->depth,1);

                    // Convert the loaded image into this new image (to grayscale)
                    if(Img->depth == IPL_DEPTH_8U ||
                       Img->depth == IPL_DEPTH_16U ||
                       Img->depth == IPL_DEPTH_32F)
                    {
                        cvCvtColor(Img,Img2,CV_BGR2GRAY);
                    }
                    else
                    {
                        CvScalar Color;
                        for(int i = 0; i < this->size1(); ++i)
                        {
                            for(int j = 0; j < this->size2(); ++j)
                            {
                                Color = cvGet2D(Img,i,j);
                                cvSet2D(Img2,i,j,cvScalar(0.114*Color.val[0] + 0.587*Color.val[1] + 0.299*Color.val[2]));
                            }
                        }
                    }

                    // Convert the new grayscale image to this object's image
                    this->ConvertScale(Img2,(*this));

                    // Clean up the resources
                    if(Img2)
                        cvReleaseImage(&Img2);
                }
            }
            else if((this->GetNumOfChannels() == 3 && Img->nChannels == 1) ||
                    (this->GetNumOfChannels() == 4 && Img->nChannels == 1))
            {
                if(this->GetDepth() == Img->depth)
                {
                    // Depths are the same so we just use the convert color function
                    if(Img->depth == IPL_DEPTH_8U ||
                       Img->depth == IPL_DEPTH_16U ||
                       Img->depth == IPL_DEPTH_32F)
                    {
                        cvCvtColor(Img,(*this),CV_GRAY2RGB);
                    }
                    else
                    {
                        CvScalar Color;
                        for(int i = 0; i < this->size1(); ++i)
                        {
                            for(int j = 0; j < this->size2(); ++j)
                            {
                                Color = cvGet2D(Img,i,j);
                                cvSet2D((*this),i,j,cvScalar(Color.val[0],Color.val[0],Color.val[0]));
                            }
                        }
                    }
                }
                else
                {
                    // The depths are different so we have to create an intermediary
                    // image to take care of the two conversions

                    // Create a new temporary image from the loaded one
                    IplImage* Img2 = cvCreateImage(cvSize(Img->width,Img->height),Img->depth,this->GetNumOfChannels());

                    // Convert the loaded image into this new image (to grayscale)
                    // Depths are the same so we just use the convert color function
                    if(Img->depth == IPL_DEPTH_8U ||
                       Img->depth == IPL_DEPTH_16U ||
                       Img->depth == IPL_DEPTH_32F)
                    {
                        cvCvtColor(Img,Img2,CV_GRAY2RGB);
                    }
                    else
                    {
                        CvScalar Color;
                        for(int i = 0; i < this->size1(); ++i)
                        {
                            for(int j = 0; j < this->size2(); ++j)
                            {
                                Color = cvGet2D(Img,i,j);
                                cvSet2D(Img2,i,j,cvScalar(Color.val[0],Color.val[0],Color.val[0]));
                            }
                        }
                    }

                    // Convert the new grayscale image to this object's image
                    this->ConvertScale(Img2,(*this));

                    // Clean up the resources
                    if(Img2)
                        cvReleaseImage(&Img2);
                }
            }
            else if((this->GetNumOfChannels() == 1 && Img->nChannels == 2))
            {
                // At this point we know that we were passed a
                // complex type image and have to save it to
                // a single-channel image

                // Color variable used to get the pixel color
                CvScalar Color;

                // Loop through the image pixel by pixel and convert
                for(int i = 0; i < this->size1(); ++i)
                {
                    for(int j = 0; j < this->size2(); ++j)
                    {
                        // Get the color
                        Color = cvGet2D(Img,i,j);

                        // Depending on the passed parameter assign the pixel's value
                        switch(InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2)
                        {
                        case 0:

                            // In this case we just copy the real value
                            cvSet2D((*this),i,j,cvScalar(Color.val[0]));

                            break;

                        case 1:

                            // In this case we just copy the imaginary value
                            cvSet2D((*this),i,j,cvScalar(Color.val[1]));

                            break;

                        case 2:

                            // In this case, we assign the absolute value to this image's pixel
                            cvSet2D((*this),i,j,cvScalar(std::sqrt(Color.val[0]*Color.val[0] + Color.val[1]*Color.val[1])));

                            break;

                        default:

                            // As a default we just copy the real value
                            cvSet2D((*this),i,j,cvScalar(Color.val[0]));

                            break;
                        }
                    }
                }
            }
            else if((this->GetNumOfChannels() == 3 && Img->nChannels == 2) ||
                    (this->GetNumOfChannels() == 4 && Img->nChannels == 2))
            {
                // At this point we know that we were passed a
                // complex type image and have to save it to
                // a three-channel or four-channel image

                // Color variable used to get the pixel color
                CvScalar Color;

                // Loop through the image pixel by pixel and convert
                for(int i = 0; i < this->size1(); ++i)
                {
                    for(int j = 0; j < this->size2(); ++j)
                    {
                        // Get the color
                        Color = cvGet2D(Img,i,j);

                        // Variable used to calculate the absolute value
                        double AbsValue;

                        // Depending on the passed parameter assign the pixel's value
                        switch(InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2)
                        {
                        case 0:

                            // In this case we just copy the real value
                            cvSet2D((*this),i,j,cvScalar(Color.val[0],Color.val[0],Color.val[0]));

                            break;

                        case 1:

                            // In this case we just copy the imaginary value
                            cvSet2D((*this),i,j,cvScalar(Color.val[1],Color.val[1],Color.val[1]));

                            break;

                        case 2:

                            // In this case, we assign the absolute value to this image's pixel
                            AbsValue = std::sqrt(Color.val[0]*Color.val[0] + Color.val[1]*Color.val[1]);
                            cvSet2D((*this),i,j,cvScalar(AbsValue,AbsValue,AbsValue));

                            break;

                        default:

                            // As a default we just copy the real value
                            cvSet2D((*this),i,j,cvScalar(Color.val[0],Color.val[0],Color.val[0]));

                            break;
                        }
                    }
                }
            }
            else if(this->GetNumOfChannels() == 2 && Img->nChannels == 1)
            {
                // At this point we know that we were passed a
                // single channel image but have to save it
                // to a complex image

                // Color variable used to get the pixel color
                CvScalar Color;

                // Loop through the image pixel by pixel and convert
                for(int i = 0; i < this->size1(); ++i)
                {
                    for(int j = 0; j < this->size2(); ++j)
                    {
                        // Get the color
                        Color = cvGet2D(Img,i,j);

                        // Assign the absolute value to this image's pixel
                        cvSet2D((*this),i,j,cvScalar(Color.val[0]));
                    }
                }
            }
            else if((this->GetNumOfChannels() == 2 && Img->nChannels == 3) ||
                    (this->GetNumOfChannels() == 2 && Img->nChannels == 4))
            {
                // At this point we know that we were passed a
                // three-channel image, so we just assign the
                // average value as the real values of our complex image

                // Color variable used to get the pixel color
                CvScalar Color;

                // Loop through the image pixel by pixel and convert
                for(int i = 0; i < this->size1(); ++i)
                {
                    for(int j = 0; j < this->size2(); ++j)
                    {
                        // Get the color
                        Color = cvGet2D(Img,i,j);

                        // Assign the average value to this image's pixel
                        cvSet2D((*this),i,j,cvScalar((Color.val[0] + Color.val[1] + Color.val[2])/3.0f));
                    }
                }
            }
        }
        else
        {
            // Error
        }
    }

    return;
}
//-------------------------------------------------------------------

//-------------------------------------------------------------------
template<typename blType>
inline void blImage<blType>::LoadImageFromFile(const string& Filename,
                                               const bool& ShouldImageBeFlipped,
                                               const int& ImageFlipMode,
                                               const int& HowToReadImageColorAndDepth,
                                               const int& InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2)
{
    // Load image from file
    IplImage* img = NULL;
    img = cvLoadImage(Filename.c_str(),HowToReadImageColorAndDepth);

    // Check if the image successfully loaded
    if(img == NULL)
    {
        // Error
        return;
    }

    // Flip image if being asked to
    if(ShouldImageBeFlipped)
        cvFlip(img,img,ImageFlipMode);

    // Load the file image into this one (we want
    // to copy the image and not just link to it)
    LoadImage(img,InCaseOfComplexToRealConversionDoYouWantReal_0_Imaginary_1_OrAbsoluteValue_2);

    // Release the image that was loaded from file
    cvReleaseImage(&img);

    return;
}
//-------------------------------------------------------------------

#endif // BL_IMAGE_HPP

Using the code

Using our image structure could not be any easier. This design super-simplifies the use of IplImage* pointers.  The following code shows some of the possible uses of blImage. Notice how easy it is to access pixel data without having to worry about types and depths. Pixel access is also very fast as it provides direct access to the pixel data.

int main(int argc, char *argv[])
{
    // Create a blank 8-bit color image
    blImage&lt; blColor3&lt;unsigned char&gt; &gt; MyImage;
    // Load an image from file
    MyImage.LoadImageFromFile(&quot;C:\\tux.png&quot;);
    // Create a floating point grayscale
    // version of our colored image
    blImage&lt;float&gt; MyImage2 = MyImage;
    // We can easily access pixel data very efficiently
    for(int i = 0; i &lt; MyImage2.size1(); ++i)
        for(int j = 0; j &lt; MyImage2.size2(); ++j)
            MyImage2[i][j] += 100;
    blImage&lt;float&gt; MyImage3 = MyImage2;
    // Let's take the first derivative in both directions
    cvSobel(MyImage2,MyImage2,1,1);
    cvNamedWindow(&quot;tux&quot;,CV_WINDOW_AUTOSIZE);
    cvShowImage(&quot;tux&quot;,MyImage3);
    cvWaitKey(0);
    return 0;
}

Running this little snippet I get the following image:

Downloads

I have put all the files into a zip file which can be downloaded here.  All you have to do is extract it somewhere, let’s say in a directory called blImageAPI, and then include the blImageAPI.hpp file as follows:

#include &quot;blImageAPI/blImageAPI.hpp&quot;
using namespace blImageAPI;

Note: Everything is declared in a namespace blImageAPI, and such you would use it as: blImageAPI::blImage

blImageAPI.zip -- Ver Jun/06/2011 1:43pm (3790)

 

NOTE:  I have just published a library to easily create custom iterators, you can find it here:  blIteratorAPI library

Updates

Good Luck and have fun. I will be releasing subsequent algorithms and data structures for image analysis, so be sure to check back in and get the updated code files.

  1. I have changed the blImg.hpp file to clean out the template specialization code.  Much more efficient and clean now. I also have overloaded operators to handle colors in a natural way.
  2. I had used a virtual destructor for the blColor3 and blColor4 structures, but I had to change it to a normal destructor, as declaring it virtual added an extra 8 bytes of size to the structures.
  3. Fixed errors caused by converting pictures. Turns out that cvCvtColor function only works with input images of type “unsigned char”, “unsigned short” or “float”, but fails for the rest.
  4. Oct/28/2010 — Updated the blColor3 and blColor4 structures to include a default constructor that takes one value only. This makes them compatible with other types when writing generic image algorithms using the blImageAPI.
  5. Nov/10/2010 — Added a simple “GetImageDataCast()” function to the blImg.hpp file to get a pointer to the raw image data, but cast to the type of the image.
    1. This function allows us to get a casted pointer to the raw image data, for those of you that prefer using raw pointers.
      1. GetImageData() — returns a const char* pointer
      2. GetImageDataCast() — returns a const blType* pointer, where blType is the type of the image
  6. Nov/11/2010 — Fixed a bug in the “LoadImageFromFile” function. Now it works perfectly
  7. Nov/16/2010 — Changed the blColor4 constructor to have a default value for alpha. I had left the alpha value as a required value before.
  8. Nov/30/2010 — Added two operators to blColor3 and blColor4 to multiply and divide two colors. Now, when two colors are multiplied or divided, the individual color components are multiplied or divided.
  9. Dec/28/2010 — Added an assignment operator to the blImg class. Without it, when using the assignment operator, the copied image referred to the same IplImage, now the assignment operator clones the original image.
  10. Jan/02/2011
    1. Changed the destructors from virtual to normal
    2. Fixed a bug in the LoadImage function when converting a single channel image to a color image when the single channel image was of type double.
  11. May/25/2011
    1. Added a null deleter functor to blCleanResources so that we can wrap a pointer with a shared_ptr without calling its destructor when it goes out of scope. For example we can wrap the “this” pointer with a shared_ptr doing the following:
      // Let's say we're inside the blImage class, we
      // can send a shared_ptr of the "this" pointer doing
      // the following:
      shared_ptr< blImage<unsigned char> > MyPointer = shared_ptr< blImage<unsigned char> >(this,null_deleter());
      
    2. Added a constructor and a LoadMatrix function to the blImage class so that we can load CvMat* structures into a blImage structure. For example we can do this:
      // Let's say we have a CvMat*
      // structure from somewhere in
      // our code like this
      CvMat* MyMatrix;
      // Then later in our code we want to
      // create a blImage out of that matrix
      // we can do the following
      blImage<unsigned char> MyImage(MyMatrix);
      // Or we could do
      blImage<unsigned char> MyImage = MyMatrix;
      // Or maybe this
      blImage<unsigned char> MyImage;
      MyImage.LoadMatrix(MyMatrix);
      
    3. Added a function to blImg to get retrieve the size in byte of an image like the following:
      // We want to get the size of a blImage
      blImage<unsigned char> MyImage;
      cout << "The size of the image = " << MyImage.GetSizeInBytes() << "\n\n";
      
  12. Jun/06/2011
    1. Added a function to blImage called WrapIplImage — It wraps an IplImage with a blImage and NULLIFIES the IplImage pointer.  In case the depth and number of channels don’t match, the IplImage is copied and the IplImage pointer is released and NULLIFIED.

About Vincenzo Barbato

Known to his friends as Enzo, he's an outside-the-box engineer/researcher whose interests and expertise span many fields, including controls systems, multi-physics simulations, mechatronics, oil technologies, data analysis and machine vision just to name a few.

Refusing to grow up, he's on a continuous journey to develop simple and creative solutions that have the power of disrupting industries by optimizing systems and processes.

Married to a beautiful wife, with two beautiful daughters and two identical twin boys, his home is a never ending chaotic fountain of inspiration.

His outlook on life:

"Never blindly accept what you're told, listen, but then question, with curiosity, creativity and collaboration we can change the world"

About Enzo

Known to his friends as Enzo, he's an outside-the-box engineer/researcher whose interests and expertise span many fields, including controls systems, multi-physics simulations, mechatronics, oil technologies, data analysis and machine vision just to name a few. Refusing to grow up, he's on a continuous journey to develop simple and creative solutions that have the power of disrupting industries by optimizing systems and processes. Married to a beautiful wife, with two beautiful daughters and two identical twin boys, his home is a never ending chaotic fountain of inspiration. His outlook on life: "Never blindly accept what you're told, listen, but then question, with curiosity, creativity and collaboration we can change the world"