c++arraysmultidimensional-arrayrgbppm

Reading RGB values from a ppm file & storing them into a 2d array called "Image" using a struct (dynamic arrays)


I am trying to read from a ppm file and extract the RGB values and store them into a 2d array w/ C++. I'm pretty sure the solution is pretty simple but for the life of me, I cannot see it. All of my attempts yield a ton of errors.

Edit: Values in the array must be in column-major order. Apparently I need to store the numbers in a struct and that will store those numbers into the array.

Background knowledge:

The first parameter is the name of the file to open and read from. The second parameter is a 2d array of Pixels (structs) that hold a color value. "Width" is the width of the array (i.e. the number of columns) needed to 'travel' across the array. "Height" is the height of the array (i.e. the number of rows) needed to 'travel' across the array

Code I have so so far:

#include "functions.h"
#include <cmath>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

using namespace std;

bool imageLoader(string filename, Pixel** image, int width, int height) {
    ifstream fin(filename.c_str());

    if(!fin.is_open()) {
        cout << "Error: failed to open input file - " << filename << endl;
        return false;
    }

    // get type from preamble
    char type[3];
    fin >> type;                                        // should be P3
    if((toupper(type[0]) != 'P') || (type[1] != '3')) { // check that type is correct
        cout << "Error: type is " << type << "instead of P3" << endl;
        return false;
    }

    int w = 0, h = 0;
    fin >> w >> h;
    if(w != width) { // check that width matches what was passed into the function
        cout << "Error: input width (" << width << ")does not match value in file ("
             << w << ")" << endl;
        return false;
    }

    if(h != height) { // check that height matches what was passed into the function
        cout << "Error: input width (" << height
             << ") does not match value in file (" << h << ")" << endl;
        return false;
    }

    // get maximum value from preamble
    int colorMax = 0;
    fin >> colorMax;
    if(colorMax > 255 || colorMax < 0) {
        cout << "Error: invalid color value" << colorMax << endl;
        return false;
    }

    // THIS IS WHAT I NEED HELP WITH
    // I am not sure how this is supposed to work out
    // extract rgb values and place into 2d arr

    /* errors :(
    Pixel colors;
    int colors.r[][] = {0, 0, 0};
    int colors.b[][] = {0, 0, 0};
    int colors.g[][] = {0, 0, 0};
    for (int i = 0; i < height; i++)
    {
    for (int j = 0; j < width; j++) {
        fin >> colors.r >> colors.g >> colors.g;
        cout << colors.r[i][j];

    }
              }
   */

    return true;
}

This is the header file I have included in the header file. Please do not modify. We are just focusing on imageLoader:

#ifndef FUNCTIONS_H
#define FUNCTIONS_H

#include <string>
#include "functions.h"

struct Pixel {
  int r; // red
  int g; // green
  int b; // blue
};


Pixel** createImage(int width, int height);
void deleteImage(Pixel** image, int width);



bool imageLoader(std::string filename, Pixel** image, int width, int height);

If any extra information is needed, please notify me. Thank you.


Solution

  • Reading the pixels is relatively simple, if you know how to allocate the image.

    In case you want to access the image as 2D array (and resolution is a parameter), you need to allocate an array of pointers that points the data.

    Example:

    image -> [col0, col1, col2, ...] 
             (col0, col1, col2 are pointers to columns - applies column major)
    
    col0 -> ####### (points first columns)
    col1 -> #######
    col2 -> #######
    

    See: How do I declare a 2d array in C++ using new?


    I used a sample image from here, and added one more column.

    Input image file content:

    P3
    5 4
    255
    0  0  0   100 0  0       0  0  0    255   0 255   0  0  255
    0  0  0    0 255 175     0  0  0     0    0  0    0  0  255
    0  0  0    0  0  0       0 15 175    0    0  0    0  0  255
    255 0 255  0  0  0       0  0  0    255  255 255  0  0  255
    

    Loop that reads the pixels:

    Pixel colors;
    
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++) {
            fin >> colors.r >> colors.g >> colors.b;
    
            //Column major - row index comes first.
            image[j][i] = colors;   //Copy RGB values into image
        }
    }
    

    Entire code sample:

    #include <string>
    #include <cmath>
    #include <fstream>
    #include <iostream>
    #include <sstream>
    
    struct Pixel {
        int r; // red
        int g; // green
        int b; // blue
    };
    
    
    using namespace std;
    
    
    bool imageLoader(string filename, Pixel** image, int width, int height) {
        ifstream fin(filename.c_str());
    
        if(!fin.is_open()) {
            cout << "Error: failed to open input file - " << filename << endl;
            return false;
        }
    
        // get type from preamble
        char type[3];
        fin >> type;                                        // should be P3
        if((toupper(type[0]) != 'P') || (type[1] != '3')) { // check that type is correct
            cout << "Error: type is " << type << "instead of P3" << endl;
            return false;
        }
    
        int w = 0, h = 0;
        fin >> w >> h;
        if(w != width) { // check that width matches what was passed into the function
            cout << "Error: input width (" << width << ")does not match value in file ("
                 << w << ")" << endl;
            return false;
        }
    
        if(h != height) { // check that height matches what was passed into the function
            cout << "Error: input width (" << height
                 << ") does not match value in file (" << h << ")" << endl;
            return false;
        }
    
        // get maximum value from preamble
        int colorMax = 0;
        fin >> colorMax;
        if(colorMax > 255 || colorMax < 0) {
            cout << "Error: invalid color value" << colorMax << endl;
            return false;
        }
    
        // THIS IS WHAT I NEED HELP WITH
        // I am not sure how this is supposed to work out
        // extract rgb values and place into 2d arr
    
        /* errors :(    
        int colors.r[][] = {0, 0, 0};
        int colors.b[][] = {0, 0, 0};
        int colors.g[][] = {0, 0, 0};
        */
        Pixel colors;
    
        for (int i = 0; i < height; i++)
        {
            for (int j = 0; j < width; j++) {
                fin >> colors.r >> colors.g >> colors.b;
                //cout << "(" << i << ", " << j << "): " << colors.r << " " << colors.g << " " << colors.b << endl;
    
                //Column major - row index comes first.
                image[j][i] = colors;   //Copy RGB values into image
            }
        }
    
        return true;
    }
    
    
    
    int main()
    {
        //img.ppm
        //https://www.cs.swarthmore.edu/~soni/cs35/f12/Labs/extras/01/ppm_info.html
    
        //P3
        //5 4
        //255
        //0  0  0   100 0  0       0  0  0    255   0 255   0  0  255
        //0  0  0    0 255 175     0  0  0     0    0  0    0  0  255
        //0  0  0    0  0  0       0 15 175    0    0  0    0  0  255
        //255 0 255  0  0  0       0  0  0    255  255 255  0  0  255
    
        int width = 5;    //Number of columns
        int height = 4;   //Number of rows
    
        //Allocate memory
        //////////////////////////////////////////////////////////////////////////
        //Allocate array of 128 pointers (pointer to columns - since column major is required)
        Pixel** image = new Pixel* [width];
    
        //Allocate columns (each column is 96 pixels).
        for (int j = 0; j < width; j++) {
            image[j] = new Pixel[height];
        }
        //////////////////////////////////////////////////////////////////////////
    
        bool res = imageLoader("img.ppm", image, width, height);
    
        if (!res) {
            cout << "res = " << res << endl;
        }
    
        //Free memory
        //////////////////////////////////////////////////////////////////////////
        //Delete columns.
        for (int j = 0; j < width; j++) {
            delete [] image[j];
        }
    
        //Delete image.
        delete [] image;
        //////////////////////////////////////////////////////////////////////////
    
        return 0;
    }
    

    Result (Watch window):

    -    image[0],4    0x0000000000ba2f20 {r=0 g=0 b=0 }    Pixel *
    +    [0]    {r=0 g=0 b=0 }    Pixel
    +    [1]    {r=0 g=0 b=0 }    Pixel
    +    [2]    {r=0 g=0 b=0 }    Pixel
    +    [3]    {r=255 g=0 b=255 }    Pixel
    -    image[1],4    0x0000000000ba2fc0 {r=100 g=0 b=0 }    Pixel *
    +    [0]    {r=100 g=0 b=0 }    Pixel
    +    [1]    {r=0 g=255 b=175 }    Pixel
    +    [2]    {r=0 g=0 b=0 }    Pixel
    +    [3]    {r=0 g=0 b=0 }    Pixel
    -    image[2],4    0x0000000000ba7470 {r=0 g=0 b=0 }    Pixel *
    +    [0]    {r=0 g=0 b=0 }    Pixel
    +    [1]    {r=0 g=0 b=0 }    Pixel
    +    [2]    {r=0 g=15 b=175 }    Pixel
    +    [3]    {r=0 g=0 b=0 }    Pixel
    -    image[3],4    0x0000000000ba7510 {r=255 g=0 b=255 }    Pixel *
    +    [0]    {r=255 g=0 b=255 }    Pixel
    +    [1]    {r=0 g=0 b=0 }    Pixel
    +    [2]    {r=0 g=0 b=0 }    Pixel
    +    [3]    {r=255 g=255 b=255 }    Pixel
    -    image[4],4    0x0000000000ba75b0 {r=0 g=0 b=255 }    Pixel *
    +    [0]    {r=0 g=0 b=255 }    Pixel
    +    [1]    {r=0 g=0 b=255 }    Pixel
    +    [2]    {r=0 g=0 b=255 }    Pixel
    +    [3]    {r=0 g=0 b=255 }    Pixel