使用visual C ++读取pgm文件

时间:2015-11-09 18:45:38

标签: c++ image image-processing graphics computer-vision

我想从pgm文件中读取像素值,然后计算积分图像并将结果保存到文本文件中(我使用visual studio 2012来运行代码)。但是代码有一些错误,它可以正确读取标题,显示正确的版本,注释和大小。但是pgm文件的像素值是错误的。只有前两行是正确的。有谁知道问题在哪里?

#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;
}

2 个答案:

答案 0 :(得分:1)

问题是pgm文件使用了ascii字符,但是没有0至32之间的ascii值字符。结果是您的代码适用于前两行,因为这些行在0至32之间没有任何强度值,小于33的第一个值会出现在第三行,并且您的代码会忽略该像素,因为没有任何字符可以显示该像素。因此,区别从第三行开始

答案 1 :(得分:0)

当我检查你的代码时,你有这个:

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];
    }
}

我认为你的意思是这样的:

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];
    }
}

修改

您的代码的这一部分可能会引起关注:

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;
}

当像素数据被保存为char类型时,您声明您的数组保持unsigned int,然后您尝试将此char类型保存到unsigned int数组中。如果将char更改为unsigned char,可能会有所帮助。我还在其中一个类的演示中包含了一个方法,用于在下面的TGA文件中阅读。

我正在研究你的数据读取方法,它可能会帮助你看一下我的一个类的函数定义的实现,它将从TGA文件中读取数据。这是提供的结构和函数定义。

// 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

注意: - 如果您复制并粘贴它将无法编译,因为它依赖于此处未列出或显示的其他类和库。这是我的一个3D图形引擎解决方案的有效工作代码。

正如你在这里看到的,我正在使用TGA标头的结构;我还有一个纹理对象的结构。现在所有这些可能超过你的需要;但重要的部分是看我是如何读取文件中的数据并存储它们的值。

重要的部分是我在文件流reinterpret_cast<>( )方法中使用C ++关键字read()的位置。这可以帮助您重写解析器以便在图像文件中读取,以便您正在读取和存储的数据与您期望的图像结构字节对齐。这还取决于您正在阅读的文件的结构以及您在代码中使用的图像结构。

我也有一个类似的方法来读取这个类使用的PNG文件,这取决于我在PC上安装它并链接到我的IDE的PGN库,因此可以找到在PGN文件中读取的函数。这比我在这里用TGA文件读取的功能要简单得多。

当您想要使用TGA文件时,您必须知道文件结构并编写自己的文件解析器,就像PNG文件一样,大部分工作都是在PNG库中为您完成的,您只需要做的就是以正确的顺序调用适当的函数并检查相应的错误。我没有显示loadPNG()函数。

当然,您正在阅读PGM文件,而不是TGA或PNG,但概念是相同的。在获得实际像素数据之前,您必须准确知道要读取的字节数,然后您应该知道图像的大小,如像素宽度*像素长度*每像素的字节数。标题信息应该在您阅读并将其存储到变量或结构时告诉您这一点,但这非常重要。一个例子是图像是256像素×256像素,但是还需要知道的是每个像素的宽度!每个像素是8位(1字节),16位(2字节),24位(3字节),32位(4字节)。另一个重要的事情是什么是颜色模式,如黑色和&amp;白色,灰度,RGB,RGBA,CYM等,以及存储颜色信息的顺序。此外,图像存储为不仅仅通过颜色信息反转,而是图像水平和/或垂直翻转。图像文件的标题信息中通常会有标记告诉您。也是压缩,未压缩的数据,是原始数据还是行程编码(RLE)。解析图像文件时,所有这些信息都很重要。当您使用BMP-JPEG时,它甚至会变得更复杂,因为它们还可以存储可能的调色板。

我希望这有助于为您提供指导,以便您可以在开始对图像数据进行任何处理或处理之前正确读取图像文件。