将字节数组保存为RAW文件格式

时间:2012-03-30 03:18:27

标签: c++ png

我有一个简单的程序,可以将数据从PNG读取到2D数组中。我想将该数据保存到.RAW文件,以便Raw StudioIrfanview可以查看我的程序输出到my_out.raw的原始图像。目前,如果我只是将原始二进制数据写入my_out.raw文件,那么两个应用程序都无法实际读取文件,即查看图像。我需要对下面的程序做什么才能看到图像?

读取PNG文件的代码是:

// MAIN.cpp
#include "pngfilereader.h"
#include <string>
#include <vector>

#include <fstream>

int main (int argc, char *argv[])
{
  PNGFileReader pngfr;
  if (!pngfr.decompress_png_to_raw(std::string("/home/matt6809/Downloads"
    "/City.png"))) {
    std::cout << "File decompression error: " << std::endl;
  } else {
    std::ofstream out;
    out.open("./my_out.raw", std::ios_base::out);
    std::vector<std::vector<unsigned char> > data;
    pngfr.get_image_data(data);
    typedef std::vector<std::vector<unsigned char> >::iterator row_it;
    typedef std::vector<unsigned char>::iterator col_it;

    for(row_it rit= data.begin(); rit != data.end(); ++rit) {
      for(col_it cit = rit->begin(); cit != rit->end(); ++cit) {
        out << (*cit);
      }   
    }   
    out << std::endl;
  }
  return 0;
}


#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <png.h>
#include <iostream>
#include <vector>
#include <string>

class PNGFileReader
{
  public:
    PNGFileReader();
    ~PNGFileReader();
    // Public exposed API:
    bool compress_raw_to_png(uint8_t data, int size);
    bool decompress_png_to_raw(const std::string &path);

    // Getters
    long unsigned int get_image_width();
    long unsigned int get_image_height();
    void get_image_data(std::vector<std::vector<unsigned char> > &data);

  private:
    // Helper functions:
    bool read_png(const std::string &path);
    bool create_png_structs(FILE *fp);
    bool free_data();
    bool alloc_data();

    // Member variables:
    png_structp m_pPNG;
    png_infop m_pPNGInfo;
    png_infop m_pPNGEndInfo;
    png_bytepp m_Data;
    long unsigned int m_ImageWidth;
    long unsigned int m_ImageHeight;

    // Enums
    enum PNGBOOL {NOT_PNG, PNG};
    enum PNGERRORS {ERROR, SUCCESS};
};


#include "pngfilereader.h"
#include <stdexcept>

PNGFileReader::PNGFileReader() :
  m_pPNG(NULL),
  m_pPNGInfo(NULL),
  m_pPNGEndInfo(NULL),
  m_Data(NULL),
  m_ImageWidth(0),
  m_ImageHeight(0)
{
}

PNGFileReader::~PNGFileReader()
{
  for (unsigned long int i = 0; i < m_ImageHeight; ++i) {
    if (m_Data[i]) {
      delete m_Data[i];
      m_Data[i] = NULL;
    }   
  }
  if (m_Data) {
    delete m_Data;
    m_Data = NULL;
  }
}

// Public Exposed API
bool PNGFileReader::compress_raw_to_png(uint8_t m_Data, int size)
{
  return PNGFileReader::SUCCESS;
}

bool PNGFileReader::decompress_png_to_raw(const std::string &path)
{
  return read_png(path);
}

// Getters
long unsigned int PNGFileReader::get_image_width()
{
  return m_ImageWidth;
}

long unsigned int PNGFileReader::get_image_height()
{
  return m_ImageHeight;
}

void PNGFileReader::get_image_data(
  std::vector<std::vector<unsigned char> > &data)
{
  for (unsigned long int i = 0; i < m_ImageHeight; ++i) {
    std::vector<unsigned char> v;
    data.push_back(v);
    for (unsigned long int j = 0; j < m_ImageWidth; ++j) {
      std::vector<unsigned char> *vp = &data[i];
      vp->push_back(m_Data[i][j]);
    } 
  }
}

// Private Methods
bool PNGFileReader::read_png(const std::string &path)
{
  /* 
   * Open up the file to read (path) in binary mode
   * first so that if anything goes wrong with libpng
   * we won't have much to undo
   */
  const char *c_path = path.c_str();
  FILE *fp = fopen(c_path, "rb");
  if (!fp)
    return PNGFileReader::ERROR;

  /*
   * Read the first BYTES_TO_READ bytes from file
   * then determine if it is a png file or 
   * not. If png_sig_cmp == 0 all is okay
   */
  enum {BYTES_TO_READ = 8};
  unsigned char sig[BYTES_TO_READ];
  if (!fread(sig, 1, BYTES_TO_READ, fp)) {
    fclose(fp);
    return PNGFileReader::ERROR;
  }

  bool is_png = !png_sig_cmp(sig, 0, BYTES_TO_READ);
  if (!is_png) {
    fclose(fp);
    return PNGFileReader::ERROR;   
  }

  if (!this->create_png_structs(fp)) {
    fclose(fp);
    return PNGFileReader::ERROR;
  }

  /*
   * For error handling purposes. Set a long pointer
   * back to this function to handle all error related
   * to file IO
   */
  if (setjmp(png_jmpbuf(m_pPNG)))
  {
    png_destroy_read_struct(&m_pPNG, &m_pPNGInfo, &m_pPNGEndInfo);
    fclose(fp);
    return PNGFileReader::ERROR;
  }

  /* 
   * Set up the input code for FILE openend in binary mode,
   * and tell libpng we have already read BYTES_TO_READ btyes from 
   * signature
   */
  png_init_io(m_pPNG, fp);
  png_set_sig_bytes(m_pPNG, BYTES_TO_READ);

  /*
   * Using the lowlevel interface to lib png ...
   */
  png_read_info(m_pPNG, m_pPNGInfo);
  m_ImageHeight = png_get_image_height(m_pPNG, m_pPNGInfo);
  m_ImageWidth = png_get_rowbytes(m_pPNG, m_pPNGInfo);
  this->alloc_data();
  png_read_image(m_pPNG, m_Data);

  png_read_end(m_pPNG, NULL);
  png_destroy_read_struct(&m_pPNG, &m_pPNGInfo, &m_pPNGEndInfo);
  fclose(fp);

  return PNGFileReader::SUCCESS;
}

bool PNGFileReader::create_png_structs(FILE *fp)
{
  /* 
   * Create the pointer to main libpng struct, as well as
   * two info structs to maintain information after, and
   * prior to all operations on png m_Data. Only necessary
   * to release resource after function succeeds.
   */
  m_pPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL,
    NULL, NULL);
  if (!m_pPNG)
  {
    fclose(fp);
    return PNGFileReader::ERROR;
  }
  m_pPNGInfo = png_create_info_struct(m_pPNG);
  if (!m_pPNGInfo)
  {
    png_destroy_read_struct(&m_pPNG, (png_infopp)NULL,(png_infopp)NULL);
    fclose(fp);
    return PNGFileReader::ERROR;
  }
  m_pPNGEndInfo = png_create_info_struct(m_pPNG);
  if (!m_pPNGEndInfo)
  {
    png_destroy_read_struct(&m_pPNG, &m_pPNGInfo, (png_infopp)NULL);
    fclose(fp);
    return PNGFileReader::ERROR;
  }
  return PNGFileReader::SUCCESS;
}

bool PNGFileReader::free_data()
{
  if (m_ImageHeight == 0 || m_ImageWidth == 0)
    return PNGFileReader::ERROR;

  for (unsigned long int i = 0; i < m_ImageHeight; ++i) {
    if (m_Data[i]) {
      delete m_Data[i];
      m_Data[i] = NULL;
    }
  }
  if (m_Data) {
    delete m_Data;
    m_Data = NULL;
  }
  return PNGFileReader::SUCCESS;
}

bool PNGFileReader::alloc_data()
{
  if (m_ImageHeight == 0 || m_ImageWidth == 0)
    return PNGFileReader::ERROR;

  if (m_Data != NULL)
    this->free_data();

  m_Data = new png_bytep[m_ImageHeight]();           
  for (unsigned long int i = 0; i < m_ImageHeight; ++i) {
    m_Data[i] = NULL;
  }
  try {
    for (unsigned long int i = 0; i < m_ImageHeight; ++i) {
      m_Data[i] = new png_byte[m_ImageWidth];
    }
  }
  catch (std::bad_alloc e) {
    for (unsigned long int i = 0; i < m_ImageHeight; ++i) {
      if (m_Data[i]) {
        delete m_Data[i];
        m_Data[i] = NULL;
      }
    }
    if (m_Data) {
      delete m_Data;
      m_Data = NULL;
    }
    throw e;
  }

  return PNGFileReader::SUCCESS;
}

1 个答案:

答案 0 :(得分:5)

旨在与Raw Studio和Irfraview等摄像机图像处理程序一起使用的“原始”文件不是没有标题的图像数据的原始二进制转储。相反,“原始”绰号指的是图像具有应用于相机内的最小量图像处理的事实。例如,图像数据可能仍然是来自相机的拜耳模式CFA的单通道单色图像,或者没有应用白平衡,颜色矩阵等等。无论哪种方式,图像数据都是仍然采用标准二进制图像文件格式格式化,包括标题,数据打包方法等。示例包括Adobe的DNG文件格式(基于TIFF)或相机制造商自己的专有格式,如佳能的CR2,尼康的NEF等等。

因此,如果您希望这些原始文件处理程序读取您的“原始”文件图像数据,您必须在二进制数据规范中读取它们支持的原始文件格式,然后重新格式化原始的PNG图像数据正确。