2015-11-09 3 views
0

Я хочу прочитать значения пикселей из файла pgm, а затем вычислить интегральное изображение и сохранить результат в текстовый файл (я использую Visual Studio 2012 для запуска кода). Но были некоторые ошибки в коде, он может правильно прочитать заголовок, показывая правильную версию, комментарий и размер. Но пиксельные значения pgm-файла были неправильными. Только первые две строки были правильными. Кто-нибудь знает, где проблема?Чтение файла pgm с использованием Visual C++

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

Я знаю, что означает «интеграл» и «изображение», но я не уверен в «целостном изображении». Можете ли вы объяснить, что вы делаете? – duffymo

+0

Вы не можете получить доступ к двумерному массиву следующим образом: integ [i, j]. Он должен быть интегральным [i] [j]. –

+0

Интегральное изображение представляет собой структуру данных и алгоритм для быстрого и эффективного генерирования суммы значений в прямоугольном подмножестве сетки. Вот введение на wiki-странице: https: //en.wikipedia.org/wiki/Summed_area_table – Myliecielo

ответ

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

EDIT

Этот раздел вашего кода может вызывать озабоченность:

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

Вы объявляете ваш массив для хранения unsigned int, пока данные пикселя сохраняются в виде символа, а затем вы пытаетесь сохранить этот тип char в массив unsigned int. Это может помочь, если вы измените свой символ на символ без знака. Я также включаю демонстрацию метода для одного из моих классов для чтения в 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 Graphics Engine.

Как вы можете видеть здесь, я использую структуру для заголовка TGA; У меня также есть структура для объекта Texture. Теперь все это может быть больше, чем вам нужно; но важные части видят, как я читаю данные из файла и сохраняю их значения.

Важная часть, где я использую ключевое слово C++ reinterpret_cast<>() в потоках файлов read(). Это может помочь вам переписать ваш синтаксический анализатор для чтения в файле изображения, чтобы данные, которые вы читаете и хранящие, выровнены по строкам с той структурой изображения, которую вы ожидаете. Это также будет зависеть от структуры файла, который вы читаете, и структуры изображения, которую вы используете в своем коде.

У меня также есть аналогичный метод для чтения в PNG-файле, который использует этот класс, который зависит от библиотеки PGN, где я установлен на моем компьютере, и связан с моей IDE, поэтому функции для чтения в PGN-файле могут быть найденный. Это довольно немного проще, чем функция, которую я показал вам здесь для чтения в TGA-файле.

Если вы хотите использовать TGA-файл, вам необходимо знать структуру файла и написать собственный файловый парсер, где, как и в файле PNG, большая часть этой работы выполняется для вас в библиотеке PNG, и все, что вам нужно будет сделать вызывает соответствующие функции в правильном порядке и проверяет наличие соответствующих ошибок. Я не показывал функцию loadPNG().

Конечно, вы читаете файл PGM, а не TGA или PNG, но концепция такая же. Вы должны точно знать, сколько байтов для чтения, прежде чем вы достигнете фактических данных пикселя, тогда вы должны знать размер изображения, как в пиксельной ширине * длина пикселя * байт на пиксель. Информация заголовка должна рассказать вам об этом, так как вы читаете это и храните его в своих переменных или структурах, но это очень важно. Примером может служить изображение, скажем, 256 пикселей X 256 пикселей, однако то, что также должно быть известно, - это то, насколько широкий пиксель! Каждый пиксель 8 бит (1 байт), 16 бит (2 байта), 24 бита (3 байта), 32 бита (4 байта). Еще одна важная вещь, которую нужно знать, это то, что такое цветной рисунок, например черный & белый, серый, RGB, RGBA, CYM и т. Д., И в каком порядке сохраняется информация о цвете. Также изображение сохраняется как инвертированное не только по цветной информации, но и изображение, переворачиваемое по горизонтали или по вертикали. Обычно в заголовке файла изображения есть флаги, которые расскажут вам об этом. Кроме того, сжатые, несжатые данные - это необработанные данные или длина пробега (RLE). Вся эта информация важна при анализе файла изображения. Когда вы работаете с BMP-JPEG, это даже немного усложняется, потому что у них также есть возможная цветовая палитра.

Я надеюсь, что это поможет вам служить в качестве руководства, чтобы вы могли правильно прочитать в своем файле изображения, прежде чем приступать к обработке или работе с данными изображения.

+0

@Myliecielo Я отредактировал свой первоначальный ответ и добавил для вас значительный объем информации. Вы должны использовать это как руководство и сообщить мне, что вы можете придумать. –

+0

Возможно, вы используете функции или стили, которые не входят в стандарты C++ 11. Некоторые функции или методы в конечном итоге становятся устаревшими из-за более новой более безопасной версии или удаляются вместе с заменой другой функции или объекта класса. –

+0

Очередная вещь, которую я вижу, заключается в том, что в верхней части вашего кода после того, как вы включаете, вы используете 'using namespace std;' в глобальной области; это плохая практика! Лучше удалить эту одну строку кода и просто просто использовать 'std :: *' в вашем источнике. В дальнейшем это вызовет меньше головных болей, особенно когда вы начнете использовать несколько библиотек с несколькими пространствами имен! –

Смежные вопросы