【问题标题】:Reading a pgm file using visual C++使用 Visual C++ 读取 pgm 文件
【发布时间】:2016-02-10 12:07:03
【问题描述】:

我想从 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;
}

【问题讨论】:

  • 我知道“integral”和“image”是什么意思,但我不确定“integral image”。你能解释一下你在做什么吗?
  • 你不能像这样访问二维数组:integral[i, j]。它应该是整数[i][j]。
  • 积分图像是一种数据结构和算法,用于在网格的矩形子集中快速有效地生成值的总和。以下是wiki网页上的介绍:en.wikipedia.org/wiki/Summed_area_table
  • 我已经更改了二维矩阵的索引,但是之后程序(我使用dev-c++编译代码)显示错误消息框然后自动关闭。

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


【解决方案1】:

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

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 Header 的结构;我还有一个纹理对象的结构。现在所有这些可能都超出了您的需要;但重要的部分是了解我如何从文件中读取数据并存储它们的值。

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

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

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

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

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

【讨论】:

  • @Myliecielo 我已经编辑了我的初始答案并添加了大量信息供您查看。你应该以此为指导,让我知道你能想出什么。
  • 您可能正在使用不属于 c++ 11 标准的函数或样式。一些函数或方法最终会因为更新更安全的版本而被弃用,或者随着替换不同的函数或类对象一起被删除。
  • 哦,我看到的另一件事是,在您的包含之后,您的代码顶部有 using namespace std; 在全局范围内;这是不好的做法!最好删除这一行代码,简单地在整个源代码中使用std::*。以后会少一些令人头疼的问题,尤其是当您开始使用具有多个命名空间的多个库时!
  • 我已经修改了代码,现在我可以在visual studio 2012上运行代码了,没有分段错误,可以正确读取文件头,现在唯一的问题是像素值仍然不对,只有前两行是正确的。
  • 之前用matlab读取pgm文件,matlab读取的像素值和visual c++读取的像素值不匹配(只有前两行是一样的),有没有可能使用不同的软件时像素值会有所不同吗?
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-28
    • 1970-01-01
    相关资源
    最近更新 更多