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::ptrdiff_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::ptrdiff_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);
}; 8 2484
> * 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?
class view;
class image
{
public:
view& operator(view); // implement in .cpp file
}
* 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.
Do not store pointers to the image's memory, but provide a function
that returns a pointer to the memory - temporarily.
* 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.
why not deriving both from a class: image_base ?
-Gernot
Gernot Frisch wrote: * 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?
class view; class image { public: view& operator(view); // implement in .cpp file }
Is this approach better then providing the additional constructors? It
is probably a typo, but shouldn't this operator return by value instead
of by reference? * 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.
Do not store pointers to the image's memory, but provide a function that returns a pointer to the memory - temporarily.
That would solve the resizing. But it still won't work if the original
image is destroyed. And to make this work, the view should contain a
reference (or pointer) to the original image. But this will prevent me
from creating a 2D view from a 3D image/view. Currently I'm thinking
about some reference counting mechanism. * 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.
why not deriving both from a class: image_base ?
I don't see how this could help. To be usefull, every common member
function (indexing, mathematical operators,...) should be implemented in
the image_base class. But how does this class know if it should access
the memory continuous (image) or with strides (view)? Or am I wrong?
Could you provide a small example?
class view;
class image
{
public:
view& operator(view); // implement in .cpp file
void AddView(view* pView);
void DeleteView(view* pView) {pView->pImage=NULL;}
view& GetView(int index);
private:
int num_views;
view* pViews;
}
class view
{
public:
view(image* pImg) {pImage=pImg; pImg->AddView(this);}
bool IsValidView() {return pImage ? true:false;}
image* GetAttachedImage() {return pImage;}
void SetView(int x, int y, int width, int height);
image GetViewedImage(); // image in range x,y,width, height
image operator(image){return GetViewedImage();}
private:
image* pImage;
}
image img1, img2;
view vw1(img1);
You have an image, and can have attached a set of views. A view can
only exist with an image (See IsValid function of view).
An image can exist without a view.
Is this what you want? Don't I understand you correctly?
Gernot Frisch wrote: class view; class image { public: view& operator(view); // implement in .cpp file void AddView(view* pView); void DeleteView(view* pView) {pView->pImage=NULL;} view& GetView(int index); private: int num_views; view* pViews; }
class view { public: view(image* pImg) {pImage=pImg; pImg->AddView(this);} bool IsValidView() {return pImage ? true:false;} image* GetAttachedImage() {return pImage;} void SetView(int x, int y, int width, int height); image GetViewedImage(); // image in range x,y,width, height image operator(image){return GetViewedImage();}
private: image* pImage; }
image img1, img2; view vw1(img1);
You have an image, and can have attached a set of views. A view can only exist with an image (See IsValid function of view). An image can exist without a view.
Is this what you want? Don't I understand you correctly?
This is more or less correct. An image contains its own (continuous)
memory and has some member functions to manipulate this memory. A view
is used to look at the memory of this image in a different way (e.g.
subimage), or the same way. The view should have the same member
functions as the image, but they should operate on the data in the view.
Only the copy constructor of the view should behave different (not the
underlying data, but the way of viewing should be copied).
An example for a small 4 x 4 image that look like this:
1 1 2 2
1 1 2 2
3 3 4 4
3 3 4 4
// Create 2 subimages of size 2x2
range ra(0,2),rb(2,4);
view va = img(ra,ra); // Upper left part
view vb = img(rb,rb); // Lower right part
view vc = vb; // Lower right part
va = 0;
// Original image will now contain:
0 0 2 2
0 0 2 2
3 3 4 4
3 3 4 4
va = vb;
// Original image will now contain:
4 4 2 2
4 4 2 2
3 3 4 4
3 3 4 4
img(0,0) = 0;
img(1,1) = 0;
vc(0,0) = 1;
vc(1,1) = 1;
// Original image will now contain:
0 4 2 2
4 0 2 2
3 3 1 4
3 3 4 1
> This is more or less correct. An image contains its own (continuous) memory and has some member functions to manipulate this memory. A view is used to look at the memory of this image in a different way (e.g. subimage), or the same way. The view should have the same member functions as the image, but they should operate on the data in the view. Only the copy constructor of the view should behave different (not the underlying data, but the way of viewing should be copied).
So, implement the image class's functions. Then do:
((image)a_view).call_image_function();
That's what you want, no? You only have to code stuff once for the
image, if the view has an image cast operator.
-Gernot
Gernot Frisch wrote: This is more or less correct. An image contains its own (continuous) memory and has some member functions to manipulate this memory. A view is used to look at the memory of this image in a different way (e.g. subimage), or the same way. The view should have the same member functions as the image, but they should operate on the data in the view. Only the copy constructor of the view should behave different (not the underlying data, but the way of viewing should be copied).
So, implement the image class's functions. Then do:
((image)a_view).call_image_function();
That's what you want, no? You only have to code stuff once for the image, if the view has an image cast operator.
This won't work, because casting the view to an image should create a
new image (with its own memory). Applying the call_image_function on
this new image will not modify the memory of the original image anymore.
My small example image again:
image ima =
1 1 2 2
1 1 2 2
3 3 4 4
3 3 4 4
// Create a view to the upper left part
view v = img(range(0,2),range(0,2));
// Cast view to a new image
image imb = v;
// The content of imb should be a real copy
// of the upper left part of the original image.
// 1 1
// 1 1
// Changing imb should leave the original image intact.
imb = 0;
// Results in
imb =
0 0
0 0
ima =
1 1 2 2
1 1 2 2
3 3 4 4
3 3 4 4
"Jef Driesen" <je********@hotmail.com.nospam> schrieb im Newsbeitrag
news:cs*********@ikaria.belnet.be... Gernot Frisch wrote:This is more or less correct. An image contains its own (continuous) memory and has some member functions to manipulate this memory. A view is used to look at the memory of this image in a different way (e.g. subimage), or the same way. The view should have the same member functions as the image, but they should operate on the data in the view. Only the copy constructor of the view should behave different (not the underlying data, but the way of viewing should be copied).
So, implement the image class's functions. Then do:
((image)a_view).call_image_function();
That's what you want, no? You only have to code stuff once for the image, if the view has an image cast operator.
This won't work, because casting the view to an image should create a new image (with its own memory). Applying the call_image_function on this new image will not modify the memory of the original image anymore.
OK. Then make the image be accessable through a view only. If you want
to operate on the image directly, cast the image to a view and use the
view's functions (fullsize) for manipulating the pixels.
-Gernot
Gernot Frisch wrote: "Jef Driesen" <je********@hotmail.com.nospam> schrieb im Newsbeitrag news:cs*********@ikaria.belnet.be...
Gernot Frisch wrote:
This is more or less correct. An image contains its own (continuous) memory and has some member functions to manipulate this memory. A view is used to look at the memory of this image in a different way (e.g. subimage), or the same way. The view should have the same member functions as the image, but they should operate on the data in the view. Only the copy constructor of the view should behave different (not the underlying data, but the way of viewing should be copied).
So, implement the image class's functions. Then do:
((image)a_view).call_image_function();
That's what you want, no? You only have to code stuff once for the image, if the view has an image cast operator.
This won't work, because casting the view to an image should create a new image (with its own memory). Applying the call_image_function on this new image will not modify the memory of the original image anymore.
OK. Then make the image be accessable through a view only. If you want to operate on the image directly, cast the image to a view and use the view's functions (fullsize) for manipulating the pixels.
This works, but the downside is that accessing pixels in a view is
slower compared to pixels in an image. For example operator= is
implemented like this for an image and a view:
image& image::operator=(const value_type& rhs)
{
std::fill(m_data, m_data + m_datasize, rhs);
return *this;
}
view& view::operator=(const value_type& rhs)
{
for (size_type i = 0; i < m_rows; ++i) {
value_type *p = m_origin + i * m_rstride;
for (size_type j = 0; j < m_columns; ++j) {
*p = rhs; p += m_cstride;
}
}
return *this;
}
This way, there is almost no reason anymore for the image class to
exist. The only difference that will remain with this approach is the
copy constructor (shallow vs deep copy). This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
by: Code4u |
last post by:
I need to design data storage classes and operators for an image
processing system that must support a range of basic data types of
different lengths i.e. float, int, char, double. I have a...
|
by: Edward Diener |
last post by:
In Borland's VCL it was possible to divide a component into design time and
run time DLLs. The design time DLL would only be necessary when the
programmer was setting a component's properties or...
|
by: tshad |
last post by:
Many (if not most) have said that code-behind is best if working in teams -
which does seem logical.
How do you deal with the flow of the work?
I have someone who is good at designing, but...
|
by: automation |
last post by:
I have a web-page consisting of a listbox & image buttons. I want to post to the appropriate PHP script based on which list_item and image button combination is selected, where the logic is...
|
by: YellowFin Announcements |
last post by:
Introduction
Usability and relevance have been identified as the major factors
preventing mass adoption of
Business Intelligence applications. What we have today are traditional
BI tools that...
|
by: googletired |
last post by:
Hello,
I haven't made a XSL in quite sometime so i am very rusty. Basicly i want the XSL to display a defined image if one is not present in the XML.
here is my current XSL and XML will be...
|
by: sravani1 |
last post by:
This code runs like when i submit the form it takes the image and displayed and top of the image a map will displayed. But actually i want that when i give the image it checks the location in the map...
|
by: DP |
last post by:
Hi,
I'm not sure if this is the right group to ask. I am developing a
small image library and I don't know how to hide the actual path to
the image. So I go to the stock photo library websites...
|
by: Ken Fine |
last post by:
I've been living with a frustrating issue with VS.NET for some months now
and I need to figure out what the problem is. Hopefully someone has run into
the same issue and can suggest a fix. I...
|
by: Kemmylinns12 |
last post by:
Blockchain technology has emerged as a transformative force in the business world, offering unprecedented opportunities for innovation and efficiency. While initially associated with cryptocurrencies...
|
by: antdb |
last post by:
Ⅰ. Advantage of AntDB: hyper-convergence + streaming processing engine
In the overall architecture, a new "hyper-convergence" concept was proposed, which integrated multiple engines and...
|
by: Matthew3360 |
last post by:
Hi there. I have been struggling to find out how to use a variable as my location in my header redirect function.
Here is my code.
header("Location:".$urlback);
Is this the right layout the...
|
by: Arjunsri |
last post by:
I have a Redshift database that I need to use as an import data source. I have configured the DSN connection using the server, port, database, and credentials and received a successful connection...
|
by: WisdomUfot |
last post by:
It's an interesting question you've got about how Gmail hides the HTTP referrer when a link in an email is clicked. While I don't have the specific technical details, Gmail likely implements measures...
|
by: Oralloy |
last post by:
Hello Folks,
I am trying to hook up a CPU which I designed using SystemC to I/O pins on an FPGA.
My problem (spelled failure) is with the synthesis of my design into a bitstream, not the C++...
|
by: Carina712 |
last post by:
Setting background colors for Excel documents can help to improve the visual appeal of the document and make it easier to read and understand. Background colors can be used to highlight important...
|
by: BLUEPANDA |
last post by:
At BluePanda Dev, we're passionate about building high-quality software and sharing our knowledge with the community. That's why we've created a SaaS starter kit that's not only easy to use but also...
|
by: Ricardo de Mila |
last post by:
Dear people, good afternoon...
I have a form in msAccess with lots of controls and a specific routine must be triggered if the mouse_down event happens in any control.
Than I need to discover what...
| |