Load raw images

Hi to all

I am writing a program in c++ in which i load an interleaved raw image with 8 bit RGB. The problem is the loading, in fact i thought to load it in this way:

1
2
3
unsigned char bufferImmagine[dimX*dimY][3];

fread (&bufferImmagine[0][0],dimX*dimY*3,1,file);


But in this way I can only read raw images with predefined dimensions, for example 640x480. I thought to an other solution so the user can choose the dimension:

1
2
3
4
5
6
7
8
9
10
//In the image class
unsigned char** bufferImmagine;
int dimX,dimY;

//In the constructor of the class
dimX=dimensioneX;
dimY=dimensioneY;
bufferImmagine=new unsigned char* [3];

fread (bufferImmagine,dimX*dimY*3,1,file);


It works with small images, but with big images it creates an output.raw of 0 byte (in windows) or it returns segmentation fault (in mac os x).
Any idea? :(
Hi Giamma!

Obviosly you need dimX * dimY * 3 units of storage to hold your image. (For example 768 unsigned char's if you image is 16 times 16 pixels.) Your first code does exactly this.

In you second code sniplett you get an array of unsigned char pointers, which is 3 elements large. But there are two problems:
-) These three pointers are not initialized.
-) You never allocate the bulk of your array.

Since there is no boundary check, the fread overwrites memory location, which were never reserved for your progam. Some OSes will terminate your program for this "undue" behavior, other OSes will just produce garbage.

Solution: Calculate the correct amount of memory and reserve it...
Heh, the correct amount of memory is known when the user defines the dimensions so this is not a problem. For example i define 640*480---> i need 307.200 blocks of memory. But the problem is how i can define a bidimensional dynamic array. The only solution i found in internet is the code i posted
Ah, I guess you want something, where you can still use [] notation, but the size can change dynamically. Ok, got it.

Well, what about
1
2
3
unsigned char *bufferImmagine;
bufferImmagine=new unsigned char [dimX*dimY*3];
...

The downside of this is, that you have to calculate the offset inside the array yourself.

btw, don't try the following:
1
2
3
unsigned char *bufferImmagine[3];
for(int i=0; i<3; ++i)
	bufferImmagine[i]=new unsigned char [dimX*dimY];

Then you can use expressions like
 
bufferImmagine[27][2]='x';

but it is not guaranteed, that the memory is contingous. bufferImmagine[0][1] may be next to bufferImmagine[size][0] but it can be far across you address. fread assumes all three sub-arrays to be contingous!

In the ugly old times of C you would just write
 
unsigned char *bufferImmagine=(unsigned char *)malloc(dimX*dimY*3*sizeof(unsigned char));

and free it after usage.
Last edited on
I tried to insert all values into a monodimensional array (dimX*dimY*3) and it seems to work with both big and small images. Now the difficulty is to understand where red, green and blue are saved but I think pointer arithmetic will help me
It is easy to get a contiguous block of memory that you can access with another array of pointers to the first block (so you can use two []s), but to do it is a waste of memory and effort.

Why not just write yourself a function to do it?
http://www.parashift.com/c++-faq-lite/operator-overloading.html#faq-13.10

While the FAQ makes valid points, in this case, you are doing something simple enough, why not write a class?
(OK, ok, I wrote one...)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// image2d.hpp
// 2008 Michael Thomas Greer
// Released to the Public Domain

#ifndef IMAGE2D_HPP
#define IMAGE2D_HPP

#include <algorithm>

//----------------------------------------------------------------------------
struct RGB_t
  {
  unsigned char red, green, blue;
  unsigned char& operator [] (const int& index)
    {
    switch (index)
      {
      case 0: return red;
      case 1: return green;
      case 2: return blue;
      }
    throw 0;
    }
  RGB_t():
    red( 0 ), green( 0 ), blue( 0 )
    { }
  RGB_t( unsigned char red, unsigned char green, unsigned char blue ):
    red( red ), green( green ), blue( blue )
    { }
  RGB_t( const unsigned char* rgb ):
    red( rgb[ 0 ] ), green( rgb[ 1 ] ), blue( rgb[ 2 ] )
    { }
  };

//----------------------------------------------------------------------------
class Image2D
  {
  private:
    // The image data is shared among all instances of the image.
    struct ImageData
      {
      unsigned width;
      unsigned height;
      RGB_t*   data;
      unsigned ref_count;
      ImageData( unsigned width, unsigned height, RGB_t* data, unsigned ref_count ):
        width(     width     ),
        height(    height    ),
        data(      data      ),
        ref_count( ref_count )
        { }
      };
    ImageData* f_data;

    void f_free()
      {
      if (f_data != NULL)
      if (--(f_data->ref_count) == 0)
        {
        delete [] f_data->data;
        delete    f_data;
        }
      f_data = NULL;
      }

    ImageData* f_getdata()
      {
      if (f_data == NULL) throw 0;
      return f_data;
      }

  public:
    // Defaultly constructed images can only be assigned to
    Image2D():
      f_data( NULL )
      { }

    // Create a new image of (width, height) colors
    Image2D( unsigned width, unsigned height ):
      f_data( new ImageData( width, height, new RGB_t[ width * height ], 1 ) )
      { }

    // Destroy image data only if we are the last referent
    ~Image2D()
      {
      f_free();
      }

    // Copy constructors
    Image2D( const Image2D& image2d )
      {
      *this = const_cast<Image2D&>( image2d );
      }

    Image2D( Image2D& image2d )
      {
      *this = image2d;
      }

    // Assignment operators
    Image2D& operator = ( const Image2D& image2d )
      {
      return *this = const_cast<Image2D&>( image2d );
      }

    Image2D& operator = ( Image2D& image2d )
      {
      if (&image2d != this)
        {
        f_free();
        f_data = image2d.f_data;
        f_getdata()->ref_count++;
        }
      return *this;
      }

    // Make a deep copy
    Image2D copy()
      {
      Image2D result( f_getdata()->width, f_getdata()->height );
      result.f_data->width     = f_data->width;
      result.f_data->height    = f_data->height;
      result.f_data->ref_count = f_data->ref_count;
      std::copy(
        f_data->data,
        f_data->data + (f_data->width * f_data->height),
        result.f_data->data
        );
      return result;
      }

    // Access the data directly
    // It is stored in row-major format (height rows of width colors).
    RGB_t*       data()       { return f_getdata()->data; }
    const RGB_t* data() const { return f_getdata()->data; }

    // Index an individual color
    RGB_t& index( unsigned x, unsigned y )
      {
      if ((x >= f_getdata()->width) or (y >= f_getdata()->height)) throw 0;
      return f_data->data[ (y * f_data->height) + x ];
      }
    const RGB_t& index( unsigned x, unsigned y ) const
      {
      if ((x >= f_getdata()->width) or (y >= f_getdata()->height)) throw 0;
      return f_data->data[ (y * f_data->height) + x ];
      }

    // Information about the image
    unsigned width()  const { return (f_data == NULL) ? 0 : f_data->width; }
    unsigned height() const { return (f_data == NULL) ? 0 : f_data->height; }
    unsigned size()   const { return f_getdata()->width * f_getdata()->height; }

    // Play with the [] operators
    struct curry
      {
      Image2D* image2d;
      unsigned x;
      curry( Image2D* image2d, unsigned x ):
        image2d(image2d),
        x(x)
        { }
      RGB_t& operator [] ( unsigned y )
        {
        return image2d->index( x, y );
        }
      const RGB_t& operator [] ( unsigned y ) const
        {
        return image2d->index( x, y );
        }
      };

    curry operator [] ( unsigned x )
      {
      return curry( this, x );
      }
    const curry operator [] ( unsigned x ) const
      {
      return curry( const_cast<Image2D*>( this ), x );
      }
  };

#endif

// end image2d.hpp 

This creates a 2D image that you can pass around. If you want to duplicate the image, use the copy() member function.

The only trick is you have to know the size of your image when you first create it. Now you can access it all kinds of ways:

1
2
3
4
5
6
7
8
9
Image2D a( 4, 3 );

a.index( 0, 0 ) = RGB_t( 255, 0, 0 );
a.index( 0, 0 ).red = 192;
a[ 0 ][ 0 ].red = 127;
a[ 0 ][ 0 ][ 0 ] = 63;
a.index( 0, 0 )[ 0 ] = 0;
a.data->red = 255;
...


Heh, enjoy!

[edit] fixed some stupid bugs...
Last edited on
Topic archived. No new replies allowed.