0

I want to read the pixel values from a pgm file, then compute the integral image and save the result to a text file (I'm using visual studio 2012 to run the code). But there were some errors about the code, it can read the header correctly, showing the right version, comment, and size. But the pixel values of the pgm file were wrong. Only the first two rows were correct. Does anyone know where the problem is?

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>

using namespace std;

int main()
{
int row = 0, col = 0, num_of_rows = 0, num_of_cols = 0;
stringstream ss;    
ifstream infile("testfile.pgm", ios::binary);

string inputLine = "";

getline(infile,inputLine);      // read the first line : P5
if(inputLine.compare("P5") != 0) cerr << "Version error" << endl;
cout << "Version : " << inputLine << endl;

getline(infile,inputLine);  // read the second line : comment
cout << "Comment : " << inputLine << endl;

ss << infile.rdbuf();   //read the third line : width and height
ss >> num_of_cols >> num_of_rows;
cout << num_of_cols << " columns and " << num_of_rows << " rows" << endl;

int max_val;  //maximum intensity value : 255
ss >> max_val;
cout<<max_val;

unsigned char pixel;

int **pixel_value = new int*[num_of_rows];
for(int i = 0; i < num_of_rows; ++i) {
    pixel_value[i] = new int[num_of_cols];
}

int **integral = new int*[num_of_rows];
for(int i = 0; i < num_of_rows; ++i) {
    integral[i] = new int[num_of_cols];
}

for (row = 0; row < num_of_rows; row++){    //record the pixel values
    for (col = 0; col < num_of_cols; col++){
         ss >> pixel;
         pixel_value[row][col]= pixel;
    }
}


integral[0][0]=pixel_value[0][0];    
for(int i=1; i<num_of_cols;i++){            //compute integral image
    integral[0][i]=integral[0][i-1]+pixel_value[0][i];      
}   
for (int i=1;i<num_of_rows; i++){
    integral[i][0]=integral[i-1][0]+pixel_value[i][0];
}
    for (int i = 1; i < num_of_rows; i++){  
    for (int j = 1; j < num_of_cols; j++){
    integral[i][j] = integral[i - 1 ][j] + integral [i][j - 1] - integral[i - 1] [j - 1] + pixel_value[i][j];       
    }
}

ofstream output1("pixel_value.txt");  // output the intensity values of the pgm file
for (int k=0; k<num_of_rows; k++)
{
    for (int r=0; r<num_of_cols; r++)
    {
        output1 << pixel_value[k][r] << " ";
    }
    output1 << ";" << endl;
}

ofstream output2("integral_value.txt");    // output the integral image
for (int a=0; a<num_of_rows; a++)
{
    for (int b=0; b<num_of_cols; b++)
    {
        output2 << integral[a][b] << " ";
    }
    output2 << ";" << endl;
}

for(int i = 0; i < num_of_rows; ++i) {
    delete [] pixel_value[i];
}
delete [] pixel_value;

for(int i = 0; i < num_of_rows; ++i) {
    delete [] integral[i];
}
delete [] integral;

infile.close();  
system("pause");
return 0;
}
Myliecielo
  • 1
  • 1
  • 3
  • I know what "integral" and "image" mean, but I'm not sure about "integral image". Can you explain what you're doing? – duffymo Nov 09 '15 at 18:52
  • You can't access a 2D array like this: integral[i, j]. It should be integral[i][j]. – Anon Mail Nov 09 '15 at 18:54
  • An integral image is a data structure and algorithm for quickly and efficiently generating the sum of values in a rectangular subset of a grid. Here is the introduction on wiki webpage:https://en.wikipedia.org/wiki/Summed_area_table – Myliecielo Nov 10 '15 at 00:02
  • I have changed the index of the 2d matrix, but after this the program(I used dev-c++ to compile the code) showed an error message box and then shut down automatically. – Myliecielo Nov 10 '15 at 00:38

2 Answers2

1

The issue is that a pgm file uses ascii characters, but there is no character for ascii values from 0 to 32. as the result your code works for two first rows because those rows do not have any intensity value between 0 and 32, the first value less than 33 appears in third line and your code ignore that pixel because there is no characer for showing that. So the differnce starts from the third row

0

When I checked your code you have this:

for (int j = 0; j < num_of_rows; j++){  //compute integral image
    for (int i = 0; i < num_of_cols; i++){
        integral[i, j] = integral[i - 1, j] + 
                         integral[i, j - 1] - 
                         integral[i - 1, j - 1] + 
                         pixel_value[i, j];
    }
}

I'm thinking you meant for it to be like this:

for (int j = 0; j < num_of_rows; j++){  //compute integral image
    for (int i = 0; i < num_of_cols; i++){
        integral[i, j] = integral[i - 1][j] + 
                         integral[i][j - 1] - 
                         integral[i - 1][j - 1] + 
                         pixel_value[i][j];
    }
}

EDIT

It is this section of your code that may be of concern:

int max_val;  //255
ss >> max_val;
cout<<max_val;
char pixel;
unsigned int pixel_value[num_of_rows][num_of_cols];
unsigned int integral[num_of_rows][num_of_cols];

for (row = 0; row < num_of_rows; row++){    //pixel values
    for (col = 0; col < num_of_cols; col++){
         ss >> pixel;
         pixel_value[row][col]= pixel;
    }
    cout << endl;
}

You are declaring your array to hold unsigned int while the pixel data is being saved into a char type, then you try to save this char type into an array of unsigned int. It might help if you change your char to an unsigned char. I am also including a demonstration of a method to one of my classes for reading in a TGA file below.

I am looking at your approach as to how your are reading in your data and it might help you to take a look at the implementation of a function definition that I have for one of my classes that will read data from a TGA file. Here is the provided structures and function definition.

// TextureInfo -------------------------------------------------------------
struct TextureInfo {
    enum FilterQuality {
        FILTER_NONE = 1,
        FILTER_GOOD,
        FILTER_BETTER,
        FILTER_BEST
    }; // FilterQuality

    unsigned    uTextureId;
    bool        hasTransparency;
    glm::uvec2  size;

    TextureInfo() :
      uTextureId( INVALID_UNSIGNED ),
      hasTransparency( false ),
      size( glm::uvec2( 0, 0 ) )
    {}
}; // TextureInfo

// -------------------------------------------------------------------------
// Texture
struct Texture {
    bool        hasAlphaChannel;
    bool        generateMipMap;
    bool        wrapRepeat;
    unsigned    uWidth;
    unsigned    uHeight;
    TextureInfo::FilterQuality filterQuality;
    std::vector<unsigned char> vPixelData;

    Texture( TextureInfo::FilterQuality filterQualityIn, bool generateMipMapIn, bool wrapRepeatIn ) :
      hasAlphaChannel( false ),
      generateMipMap( generateMipMapIn ),
      wrapRepeat( wrapRepeatIn ),
      uWidth( 0 ),
      uHeight( 0 ),
      filterQuality( filterQualityIn )
    {}
}; // Texture

// -------------------------------------------------------------------------
// loadTga()
void TextureFileReader::loadTga( Texture* pTexture ) {
    if ( nullptr == pTexture ) {
        throw ExceptionHandler( __FUNCTION__ + std::string( " invalid pTexture passed in" ) );
    }

    struct TgaHeader {
        unsigned char   idLength;
        unsigned char   colorMapType;
        unsigned char   imageType;
        unsigned char   colorMapSpecifications[5];
        short           xOrigin;
        short           yOrigin;
        short           imageWidth;
        short           imageHeight;
        unsigned char   pixelDepth;
        unsigned char   imageDescriptor;
    } tgaHeader;

    enum TgaFileType {
        TGA_RGB     = 2,
        TGA_RLE_RGB = 10,
    }; // TgaFileType

    // Error Message Handling
    std::ostringstream strStream;
    strStream << __FUNCTION__ << " ";

    // Open File For Reading
    m_fileStream.open( m_strFilenameWithPath, std::ios_base::in | std::ios_base::binary );
    if ( !m_fileStream.is_open() ) {
        strStream << "can not open file for reading";
        throwError( strStream );
    }

    // Get TGA File Header
    if ( !m_fileStream.read( reinterpret_cast<char*>( &tgaHeader ), sizeof( tgaHeader ) ) ) {
        strStream << "error reading header";
        throwError( strStream );
    }

    // This TGA File Loader Can Only Load Uncompressed Or Compressed True-Color Images
    if ( (tgaHeader.imageType != TGA_RGB ) && (tgaHeader.imageType != TGA_RLE_RGB ) ) {
        strStream << "TGA loader only supports loading RGB{" << TGA_RGB << "} and RLE_RGB{" << TGA_RLE_RGB
        << "} encoded files. This file contains pixels encoded in an unsupported type{" << tgaHeader.imageType << "}";
        throwError( strStream );
    }

    // Convert Bits Per Pixel To Bytes Per Pixel
    unsigned uBytesPerPixel = tgaHeader.pixelDepth / 8;
    if ( (uBytesPerPixel != 3) && (uBytesPerPixel != 4) ) {
        strStream << "TGA loader only supports 24bpp or 32bpp images. This image uses " << tgaHeader.pixelDepth << " bits per pixel";
        throwError( strStream );
    }

    // Make Room For All Pixel Data
    if ( 0 == tgaHeader.imageWidth || 0 == tgaHeader.imageHeight ) {
        strStream << "invalid image size (" << tgaHeader.imageWidth << "," << tgaHeader.imageHeight << ")";
        throwError( strStream );
    }
    unsigned uTotalNumBytes = tgaHeader.imageWidth * tgaHeader.imageHeight * uBytesPerPixel;
    pTexture->vPixelData.resize( uTotalNumBytes );

    // Move Read Pointer To Beginning Of Image Data
    if ( tgaHeader.idLength > 0 ) {
        m_fileStream.ignore( tgaHeader.idLength );
    }

    // Used To Get And Flip Pixels Data
    std::vector<unsigned char> vTempPixel( uBytesPerPixel, 0 );

    if ( tgaHeader.imageType == TGA_RLE_RGB ) {
        // TGA Data Is Compressed

        // All Error Messages The Same If Error Occurs Below
        strStream << "file is corrupted, missing pixel data";

        unsigned char ucRepetitionCounter = 0;

        unsigned uTotalNumberPixels = tgaHeader.imageWidth * tgaHeader.imageHeight;
        unsigned uCurrentPixel = 0;
        while( uCurrentPixel < uTotalNumberPixels ) {
            // Get Repetition Count Value
            if ( !m_fileStream.read( reinterpret_cast<char*>( &ucRepetitionCounter ), sizeof( unsigned char ) ) ) {
                throwError( strStream );
            }

            if ( ucRepetitionCounter < 128 ) {
                // Raw Packet. Counter Indicates How Many Different Pixels Need To Be Read
                ++ucRepetitionCounter;

                // Get Pixel Values
                if ( !m_fileStream.read( reinterpret_cast<char*>( &pTexture->vPixelData[uCurrentPixel * uBytesPerPixel] ), uBytesPerPixel * ucRepetitionCounter ) ) {
                    throwError( strStream );
                }
            } else {
                // Run-Length Packet. Counter Indicates How Many Times The Text Pixel Needs To Repeat
                ucRepetitionCounter -= 127;

                // Get Pixel Value
                if ( !m_fileStream.read( reinterpret_cast<char*>( &vTempPixel[0] ), uBytesPerPixel ) ) {
                    throwError( strStream );
                }
                // Save Pixel Multiple Times
                for ( unsigned int u = uCurrentPixel; u < ( uCurrentPixel + ucRepetitionCounter ); ++u ) {
                    memcpy( &pTexture->vPixelData[u * uBytesPerPixel], &vTempPixel[0], uBytesPerPixel );
                }
            }    

            // Increment Counter
            uCurrentPixel += ucRepetitionCounter;
        }
    } else {
       // TGA Data Is Uncompressed
       // Get Pixel Data
       if ( !m_fileStream.read( reinterpret_cast<char*>( &pTexture->vPixelData[0] ), pTexture->vPixelData.size() ) ) {
            strStream << "file is corrupted, missing pixel data";
            throwError( strStream );
        }
    }
    m_fileStream.close();

    // Convert All Pixel Data from BGR To RGB
    unsigned char ucTemp;
    for ( unsigned int u = 0; u < uTotalNumBytes; u += uBytesPerPixel ) {
        ucTemp                      = pTexture->vPixelData[u];      // Save Blue Color
        pTexture->vPixelData[u]     = pTexture->vPixelData[u + 2];  // Set Red Color
        pTexture->vPixelData[u + 2] = ucTemp;                       // Set Blue Color
    }

    // Flip Image Horizontally
    if ( tgaHeader.imageDescriptor & 0x10 ) {
        short sHalfWidth = tgaHeader.imageWidth >> 1;
        for ( short h = 0; h < tgaHeader.imageHeight; ++h ) {
            for ( short w = 0; w < sHalfWidth; ++w ) {
                unsigned uPixelLeft  = uBytesPerPixel * ( h * tgaHeader.imageWidth + w );
                unsigned uPixelRight = uBytesPerPixel * ( h * tgaHeader.imageWidth + tgaHeader.imageWidth - 1 - w );

                memcpy( &vTempPixel[0],                     &pTexture->vPixelData[uPixelLeft],  uBytesPerPixel ); // Store Left Pixel
                memcpy( &pTexture->vPixelData[uPixelLeft],  &pTexture->vPixelData[uPixelRight], uBytesPerPixel ); // Save Right Pixel @ Left
                memcpy( &pTexture->vPixelData[uPixelRight], &vTempPixel[0],                     uBytesPerPixel ); // Save Left Pixel @ Right
            }
        }
    }

    // Flip Vertically
    if ( tgaHeader.imageDescriptor & 0x20 ) {
    short sHalfHeight = tgaHeader.imageHeight >> 1;
        for ( short w = 0; w < tgaHeader.imageWidth; ++w ) {
            for ( short h = 0; h < sHalfHeight; ++h ) {
                unsigned uPixelTop    = uBytesPerPixel * ( w + tgaHeader.imageWidth * h );
                unsigned uPixelBottom = uBytesPerPixel * ( w + tgaHeader.imageWidth * ( tgaHeader.imageHeight - 1 - h ) );

                memcpy( &vTempPixel[0],                      &pTexture->vPixelData[uPixelTop],    uBytesPerPixel ); // Store Top Pixel
                memcpy( &pTexture->vPixelData[uPixelTop],    &pTexture->vPixelData[uPixelBottom], uBytesPerPixel ); // Save Bottom Pixel @ Top
                memcpy( &pTexture->vPixelData[uPixelBottom], &vTempPixel[0],                      uBytesPerPixel ); // Save Top Pixel @ Bottom
            }
        }
    }

    // Store Other Values In Texture
    pTexture->uWidth          = tgaHeader.imageWidth;
    pTexture->uHeight         = tgaHeader.imageHeight;
    pTexture->hasAlphaChannel = ( tgaHeader.pixelDepth == 32 );

} // loadTga

NOTE: - This will not be able to compile if you copy and paste for it relies on other classes and libraries not listed or shown here. This is valid working code from one of my 3D Graphics Engine Solutions.

As you can see here I am using a structure for the TGA Header; I also have a structure for a Texture Object. Now all of this may be more than what you need; but the important parts is seeing how it is that I am reading in the data from the file and storing their values.

The important part is where I am using the C++ keyword reinterpret_cast<>( ) within the file streams read() method. This may help you to rewrite your parser for reading in the image file so that the data you are reading in and storing is byte aligned with the image structure that you are expecting. This will also depend on the structure of the file that you are reading and the image structure that you are using in your code.

I also have a similar method for reading in a PNG file that this class uses which depends on the PGN Library where I have it installed on my pc and linked to my IDE so the functions for reading in a PGN File can be found. This is quite a bit more simple than the function that I have shown you here for reading in a TGA File.

When you want to use a TGA File you have to know the file structure and write your own file parser where as with a PNG file most of that work is done for you within the PNG Library and all you would have to do is call the appropriate functions in the proper order and check for the appropriate errors. I have not shown the loadPNG() function.

Of course you are reading in a PGM File and not a TGA or PNG but the concept is the same. You have to know exactly how many bytes to read in before you reach the actual pixel data, then you should know the size of the image as in pixel width * pixel length * bytes per pixel. The header information should tell you this much as you read that in and store it to your variables or structures, but this is very important. An example would be the image is say 256 pixels X 256 pixels, however what also needs to be known is how wide each pixel is! Is each pixel 8bits (1bytes), 16bits (2bytes), 24bits(3bytes), 32bits(4bytes). Another important thing to know is what is the color pattern such as black & white, gray scale, RGB, RGBA, CYM, etc., and in which order the color information is stored. Also is the image stored as being inverted not just by color information, but is the image flipped horizontally and or vertically. There are usually flags within an image file's header information that will tell you this. Also is the data compressed, uncompressed, is it raw data, or run length encoded (RLE). All of this information is important when parsing an image file. It even gets to be a little more complicated when you work with BMP - JPEG because they also have a possible color palette stored.

I hope this helps to serve you as a guide so that you can properly read in your image file before you begin to do any processing or work on the image data.

Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
  • @Myliecielo I have Edited my initial answer and added a significant amount of information for you to review. You should use this as a guide and let me know what you are able to come up with. – Francis Cugler Nov 10 '15 at 18:22
  • You may be using functions or styles that are not within the c++ 11 standards. Some functions or methods end up becoming deprecated due to a newer more secure version of it, or removed all together with a replacement of a different function or class object. – Francis Cugler Nov 19 '15 at 14:38
  • Oh another thing that I see is that at the top of your code after your includes you have `using namespace std;` in the global scope; this is bad practice! It is better to remove this one line of code and simple just use `std::*` throughout your source. It will cause less headaches later on, especially when you begin to use multiple libraries with multiple namespaces! – Francis Cugler Nov 19 '15 at 14:43
  • I have revised the code, now I can run the code on visual studio 2012, and there were no segmentation faults, the header of the file can be read correctly, now the only problem is the pixel values were still not right, only the first two rows were correct. – Myliecielo Nov 19 '15 at 15:39
  • I used matlab to read the pgm file before, the pixel values read by matlab don't match with the pixel values read by visual c++ (only the first two rows are the same), is it possible that the pixel values would be different when using different software? – Myliecielo Nov 19 '15 at 17:02
  • It depends on the data structure that the file format is saved. If the file is saved in binary and you are reading in the data from the file stream as binary then it is a matter of knowing the size of the header information before you read the actual pixel data. Also knowing the dimensions of the image is important and also how many bytes each pixel value has is also important. If you have any available documentation on the file structure of your `pgm` file then I would suggest reading it in full detail to see how the information is stored. There could be multiple types to that file type. – Francis Cugler Nov 19 '15 at 18:36
  • I found this which may be of help in opening the file for reading its contents to store into a structure or member variable fields within a function. http://fileinfo.com/extension/pgm I hope this helps you out! You may have to read in the first byte to find out if it is P2 or P5 to know if it is saved as ASCII text or Binary, then you have to find out if the pixel information is 1 or 2 bytes, then you need to know how what the maximum possible shades are stored related to this image. – Francis Cugler Nov 19 '15 at 18:50
  • Here is another link with a decent description http://people.sc.fsu.edu/~jburkardt/data/pgmb/pgmb.html – Francis Cugler Nov 19 '15 at 18:58
  • Here is a question and answers right here from stack that gives a few good representations of how to read in the data from the file as well as other things to be aware with when parsing the header information. Some files can have comments and you have to take them into consideration. http://stackoverflow.com/questions/8126815/how-to-read-in-data-from-a-pgm-file-in-c – Francis Cugler Nov 19 '15 at 19:20
  • The header of the file contains the following message: P5 # Created by IrfanView 384 288 255. So I know the format is P5, the dimension is 288*384, and since the maxval is 255, each pixel should be one byte. But I still don't understand why the first two rows are correct while the others are not. I declared the pixel to be unsigned char, shouldn't it be read 1 byte at a time? – Myliecielo Nov 20 '15 at 02:48
  • That does seem appropriate, and I've searched many sites for a good detail explanation of this file format, but the ones I listed above is what I was able to find. I'm sure there are more out there and even better explanations but personally I've never worked with PGM files. What I have read is that if you have the P5 flag which represents this as a binary data as opposed to ACSII text, there may be multiple images within the file. I'm not certain but it might also be 2 bytes wide if in Binary 1 byte if in ASCII. I would have to double check on this. – Francis Cugler Nov 20 '15 at 07:52
  • I found this text from a pgn library site: `A raster of Height rows, in order from top to bottom. Each row consists of Width gray values, in order from left to right. Each gray value is a number from 0 through Maxval, with 0 being black and Maxval being white. Each gray value is represented in pure binary by either 1 or 2 bytes. If the Maxval is less than 256, it is 1 byte. Otherwise, it is 2 bytes. The most significant byte is first.` I think you may be right on it being 1 byte if it is 255 for max byte. Where you might have your issue is with the most significant byte or endian architecture. – Francis Cugler Nov 20 '15 at 07:58