Reputation: 23
I want to read a raw file, which has 3 interleaving and has a size of about(3.5MB) large into a three dimensional array.. the code that I am using to read the file is:
ifile.open(argv[2], ios::in|ios::binary);
for(int i=0; i<1278; i++){
for(int j=0; j<968; j++){
for(int k=0; k<3; k++){
Imagedata1[i][j][k]=ifile.get();
}
}
}
The thing is this array is not what i expect it to be.. I need the 1278 to be the width of the image.. the 968 to be the height and 3 bytes are the RGB values.. how should i write the code to read from the file such that the array gets populated correctly.. Thanks.
Upvotes: 1
Views: 1426
Reputation: 67492
As long as you always access your data through the three-dimensional array it does not matter how the structure of the array is laid down in memory. In your example you define the first index (i) as the column index, the (j) index as the row index and the (k) index as the pixel component index.
I suspect your image file has the data organized in the standard way, which is an array of rows, where each row is an array of pixels, where each pixel is an array of color components. If that is the case, then you can modify your code to work with that structure:
ifile.open(argv[2], ios::in|ios::binary);
for(int i=0; i<1278; i++){
for(int j=0; j<968; j++){
for(int k=0; k<3; k++){
Imagedata1[j][i][k]=ifile.get();
}
}
}
Note that I just swapped the i and j indexes in the body of the innermost for loop, the rest of the code is identical to yours.
If you also need to access the image data as a planar buffer, then you should reorganize your arrays so that the data in memory mimics the layout from the file. For this you want the first dimension to be the rows, then columns, then color components. That would end up like this:
unsigned char Imagedata1[968][1278][3];
ifile.open(argv[2], ios::in|ios::binary);
for(int i=0; i<968; i++){
for(int j=0; j<1278; j++){
for(int k=0; k<3; k++){
Imagedata1[i][j][k]=ifile.get();
}
}
}
Update: after a quick discussion in the comments, it seems you do want to read this in a more efficient way. If you want to load this image on the stack (probably not a good idea) you would declare your image buffer as:
unsigned char Imagedata1[968][1278][3];
I would instead put it on the heap, like this:
unsigned char* Imagedata1 = new unsigned char[968*1278*3];
Then you can read the data from the file with:
if (fread(Imagedata1, 1278*3, 968, fd) != 968) {
// handle error here
}
Note that the numbers that you put as count and size in the fread call are not that important, as long as those two numbers multiplied are equal to width*height*components.
If you then need to access this image as a 3-dimensional array, I recommend that you create an auxiliary function, something like this:
unsigned char* getPixel(int col, int row)
{
return Imagedata1 + row * 1278 * 3 + col * 3;
}
The return value of this function can be used as a one dimensional array of three elements, the red, green and blue of the pixel.
As a final suggestion, I recommend that you wrap your image in a class, and add member variables that define the width and height (and maybe the number of color planes as well). You do not want to hardcode the 1278s and the 968s all over the place in your code!
I hope this helps.
Upvotes: 0
Reputation: 56078
First, image files are usually stored, in order of smallest jump to largest, color values, column, row order. You are not reading them in that order.
ifile.open(argv[2], ios::in|ios::binary);
for(int j=0; j<968; j++){
for(int i=0; i<1278; i++){
for(int k=0; k<3; k++){
Imagedata1[i][j][k]=ifile.get();
}
}
}
That is how the loop should be arranged, though you may want to rename your variables to keep things straight:
ifile.open(argv[2], ios::in|ios::binary);
for(int row=0; row<968; row++){
for(int col=0; col<1278; col++){
for(int color=0; color<3; color++){
Imagedata1[col][row][color]=ifile.get();
}
}
}
Secondly, the way you allocate your array is really broken and inefficient. Here is how it should work:
#include <iostream>
#include <fstream>
class ColorValue {
public:
ColorValue(unsigned char r, unsigned char g, unsigned char b)
: r_(r), g_(g), b_(b) {}
ColorValue() : r_(0), g_(0), b_(0) {}
unsigned char getR() const { return r_; }
unsigned char getG() const { return g_; }
unsigned char getB() const { return b_; }
private:
unsigned char r_, g_, b_;
};
void readrows(const char *fname, ColorValue imagedata[][1278])
{
::std::ifstream ifile;
ifile.open(fname, ::std::ios::in|::std::ios::binary);
for (int row = 0; row < 968; ++row) {
for (int col = 0; col < 1278; ++col) {
char r, g, b;
ifile.get(r);
ifile.get(g);
ifile.get(b);
imagedata[row][col] = ColorValue(r, g, b);
}
}
}
int main(int argc, const char *argv[])
{
ColorValue (*imagedata)[1278] = new ColorValue[968][1278];
readrows(argv[1], imagedata);
delete[] imagedata;
}
Using a class for ColorValue
keeps you from having magic indexes everywhere in your code for the 'r', 'g', and 'b' components. And allocating the array in this way keeps all the memory used for the image contiguous and removes several levels of unnecessary indirection. These both have the property of making your program much more cache friendly.
I also found a nice article that's a really comprehensive treatment of multi-dimensional arrays in C++.
Upvotes: 2
Reputation: 121819
C arrays don't work like that.
Let's say you wanted a 2D 1024*768 pixel buffer, with 4 bytes per pixel. You'd do something like this:
unsigned int pixbuf[1024*768];
for (int irow=0; irow < 1024; irow++)
for (int icol=0; icol < 768; icol++)
pixbuf[(irow*1024)+icol] = icolor;
Here is a good link that explains further:
http://c-faq.com/~scs/cclass/int/sx9.html
'Hope that helps!
Upvotes: 0