J
Jef Driesen
I'm implementing some image processing algorithms in C++. I created a
class called 'image' (see declaration below), that will take care of the
memory allocations and some basic (mathematical) stuff. The class will
behave like a std::vector (copy constructor and assignment create a deep
copy), but with 2D indexing.
Now I also need a 'view' class that will behave like a reference to an
'image' (can only be constructed from an existing image, copy
constructor creates a shallow copy and assignment operator a deep copy).
And it should have the additional property that it may contain only a
portion of the original image. I will show some examples to make clear
what I need:
// For images:
image<int> ima, imb(512,512), imc = imb; // Create some images
point p(3,3); // Create the point (3,3)
ima = 0; // Set all pixels to zero
ima = imb; // Copy all pixels
ima += 64; // Elementwise mathematical operators (+,-,* and /)
ima += imb; // Elementwise mathematical operators (+,-,* and /)
ima(0,0) = 255; // Indexing
ima(p) = 128; // Indexing with point
// For views:
range r(0,256,1) // Create the range [0,256[ with stride 1
view<int> va = ima(r,r), vb = imb(r,r); // Create subimages
view<int> vc = vb; // Create a shallow copy
va = 0; // Set all pixels (in the subimage) to zero
va = vb; // Copy all pixels (in the subimages)
.... // Indexing, mathematical operators
// For mixed images and views:
view<int> vd = ima; // Create a shallow copy
image<int> imd = vd; // Create a deep copy
ima = vb; vb = ima; // Copy all pixels
I created my first view class (see declaration below). The class does
not allocate or deallocate memory, it only keeps a pointer to the memory
obtained from the source image. But there are some problems:
* There are no conversions possible from image <--> view. I think I can
solve this with two additional constructors image(const view<T>& rhs)
and view(const image<T>& rhs)? And also for the other member functions?
* If the original image is destroyed or new memory is allocated
(resizing, assignment with different size), all associated views will
now point to invalid memory. It is also not possible to return a view
from a function, if it was created from a local image. This would be
usefull to prevent unnecessary copying, since copying an image object is
expensive, while copying a view is not.
* And the most important: I will have to duplicate every algorithm to
work on both images and views. Or even more code for mixed cases. Maybe
I could solve this by deriving the view class from the image class and
making all member functions virtual? But won't this approach slow down
the trivial functions, like operator(), due to the overhead of a virtual
function call? And these functions will be called very frequently in loops.
Any suggestions to solve these problems or to improve my classes are
welcome. Maybe there is already an existing framework that has this
functionality? Keep in mind that I will need 3D planar images as well
(extra template parameter N?).
template <typename T>
class image {
public: // Typedefs
typedef T value_type;
typedef std::size_t size_type;
typedef std:trdiff_t difference_type;
protected: // Data members
value_type *m_data;
size_type m_datasize;
size_type m_rows, m_columns;
public:
// Constructors and destructor.
image();
image(size_type m, size_type n);
image(size_type m, size_type n, const value_type& init);
image(const image<T>& rhs);
~image();
// Assignment operators.
image<T>& operator=(const image<T>& rhs);
image<T>& operator=(const value_type& rhs);
// Indexing operators.
value_type& operator()(size_type i, size_type j);
value_type& operator()(point p);
view<T> operator()(range i, range j);
const value_type& operator()(size_type i, size_type j) const;
const value_type& operator()(point p) const;
const view<T> operator()(range i, range j) const;
// Size functions.
size_type size() const;
size_type rows() const;
size_type columns() const;
// Elementwise mathematical operators
image<T>& operator+=(const image<T>& rhs);
image<T>& operator-=(const image<T>& rhs);
image<T>& operator*=(const image<T>& rhs);
image<T>& operator/=(const image<T>& rhs);
image<T>& operator+=(const value_type& rhs);
image<T>& operator-=(const value_type& rhs);
image<T>& operator*=(const value_type& rhs);
image<T>& operator/=(const value_type& rhs);
};
template <typename T>
class view {
public: // Typedefs
typedef T value_type;
typedef std::size_t size_type;
typedef std:trdiff_t difference_type;
protected: // Data members
value_type *m_origin;
size_type m_rows, m_columns;
difference_type m_rstride, m_cstride;
public:
// Constructors and destructor.
view(const view<T>& rhs);
~view();
// Assignment operators.
view<T>& operator=(const view<T>& rhs);
view<T>& operator=(const value_type& rhs);
// Indexing operators.
value_type& operator()(size_type i, size_type j);
value_type& operator()(point p);
view<T> operator()(range i, range j);
const value_type& operator()(size_type i, size_type j) const;
const value_type& operator()(point p) const;
const view<T> operator()(range i, range j) const;
// Size functions.
size_type size() const;
size_type rows() const;
size_type columns() const;
// Elementwise mathematical operators
view<T>& operator+=(const view<T>& rhs);
view<T>& operator-=(const view<T>& rhs);
view<T>& operator*=(const view<T>& rhs);
view<T>& operator/=(const view<T>& rhs);
view<T>& operator+=(const value_type& rhs);
view<T>& operator-=(const value_type& rhs);
view<T>& operator*=(const value_type& rhs);
view<T>& operator/=(const value_type& rhs);
};
class called 'image' (see declaration below), that will take care of the
memory allocations and some basic (mathematical) stuff. The class will
behave like a std::vector (copy constructor and assignment create a deep
copy), but with 2D indexing.
Now I also need a 'view' class that will behave like a reference to an
'image' (can only be constructed from an existing image, copy
constructor creates a shallow copy and assignment operator a deep copy).
And it should have the additional property that it may contain only a
portion of the original image. I will show some examples to make clear
what I need:
// For images:
image<int> ima, imb(512,512), imc = imb; // Create some images
point p(3,3); // Create the point (3,3)
ima = 0; // Set all pixels to zero
ima = imb; // Copy all pixels
ima += 64; // Elementwise mathematical operators (+,-,* and /)
ima += imb; // Elementwise mathematical operators (+,-,* and /)
ima(0,0) = 255; // Indexing
ima(p) = 128; // Indexing with point
// For views:
range r(0,256,1) // Create the range [0,256[ with stride 1
view<int> va = ima(r,r), vb = imb(r,r); // Create subimages
view<int> vc = vb; // Create a shallow copy
va = 0; // Set all pixels (in the subimage) to zero
va = vb; // Copy all pixels (in the subimages)
.... // Indexing, mathematical operators
// For mixed images and views:
view<int> vd = ima; // Create a shallow copy
image<int> imd = vd; // Create a deep copy
ima = vb; vb = ima; // Copy all pixels
I created my first view class (see declaration below). The class does
not allocate or deallocate memory, it only keeps a pointer to the memory
obtained from the source image. But there are some problems:
* There are no conversions possible from image <--> view. I think I can
solve this with two additional constructors image(const view<T>& rhs)
and view(const image<T>& rhs)? And also for the other member functions?
* If the original image is destroyed or new memory is allocated
(resizing, assignment with different size), all associated views will
now point to invalid memory. It is also not possible to return a view
from a function, if it was created from a local image. This would be
usefull to prevent unnecessary copying, since copying an image object is
expensive, while copying a view is not.
* And the most important: I will have to duplicate every algorithm to
work on both images and views. Or even more code for mixed cases. Maybe
I could solve this by deriving the view class from the image class and
making all member functions virtual? But won't this approach slow down
the trivial functions, like operator(), due to the overhead of a virtual
function call? And these functions will be called very frequently in loops.
Any suggestions to solve these problems or to improve my classes are
welcome. Maybe there is already an existing framework that has this
functionality? Keep in mind that I will need 3D planar images as well
(extra template parameter N?).
template <typename T>
class image {
public: // Typedefs
typedef T value_type;
typedef std::size_t size_type;
typedef std:trdiff_t difference_type;
protected: // Data members
value_type *m_data;
size_type m_datasize;
size_type m_rows, m_columns;
public:
// Constructors and destructor.
image();
image(size_type m, size_type n);
image(size_type m, size_type n, const value_type& init);
image(const image<T>& rhs);
~image();
// Assignment operators.
image<T>& operator=(const image<T>& rhs);
image<T>& operator=(const value_type& rhs);
// Indexing operators.
value_type& operator()(size_type i, size_type j);
value_type& operator()(point p);
view<T> operator()(range i, range j);
const value_type& operator()(size_type i, size_type j) const;
const value_type& operator()(point p) const;
const view<T> operator()(range i, range j) const;
// Size functions.
size_type size() const;
size_type rows() const;
size_type columns() const;
// Elementwise mathematical operators
image<T>& operator+=(const image<T>& rhs);
image<T>& operator-=(const image<T>& rhs);
image<T>& operator*=(const image<T>& rhs);
image<T>& operator/=(const image<T>& rhs);
image<T>& operator+=(const value_type& rhs);
image<T>& operator-=(const value_type& rhs);
image<T>& operator*=(const value_type& rhs);
image<T>& operator/=(const value_type& rhs);
};
template <typename T>
class view {
public: // Typedefs
typedef T value_type;
typedef std::size_t size_type;
typedef std:trdiff_t difference_type;
protected: // Data members
value_type *m_origin;
size_type m_rows, m_columns;
difference_type m_rstride, m_cstride;
public:
// Constructors and destructor.
view(const view<T>& rhs);
~view();
// Assignment operators.
view<T>& operator=(const view<T>& rhs);
view<T>& operator=(const value_type& rhs);
// Indexing operators.
value_type& operator()(size_type i, size_type j);
value_type& operator()(point p);
view<T> operator()(range i, range j);
const value_type& operator()(size_type i, size_type j) const;
const value_type& operator()(point p) const;
const view<T> operator()(range i, range j) const;
// Size functions.
size_type size() const;
size_type rows() const;
size_type columns() const;
// Elementwise mathematical operators
view<T>& operator+=(const view<T>& rhs);
view<T>& operator-=(const view<T>& rhs);
view<T>& operator*=(const view<T>& rhs);
view<T>& operator/=(const view<T>& rhs);
view<T>& operator+=(const value_type& rhs);
view<T>& operator-=(const value_type& rhs);
view<T>& operator*=(const value_type& rhs);
view<T>& operator/=(const value_type& rhs);
};