スポンサーリンク

PNM Reader(改)

プログラム本体(pnm_reader.hpp)

#pragma once

#include <fstream>
#include <sstream>
#include <string>
#include <vector>

#pragma warning(disable:4996)


//! @class CGetGrayBits
//! @brief 配列上のx番目の画素値を取得するクラス
//! @sa https://suzulang.com/bit-depth-1-2-4-getpixelvalue/
class CGetGrayBits {
  unsigned char* m_p;        //!< 画素へのポインタ
  const int m_depth = 1;     //!< ビット深度

  unsigned char m_mask = 1;  //!< depth8未満の時のビットマスク
public:

  //! @brief コンストラクタ
  //! @param [in] p 画素配列へのポインタ
  CGetGrayBits(
    const void* const p) :
    m_p(static_cast<unsigned char*>(const_cast<void*>(p))) {
  }

  //! @brief 指定した番号の画素を取得
  //! @param [in] x 画素番号
  //! @return 画素値
  int operator[](const std::size_t x) {
    // x ドットが何バイト目かを算出 (0開始)
    std::size_t byte = x / (8 / m_depth);

    unsigned char c = m_p[byte];

    std::size_t pos = x % (8 / m_depth);

    int shift = 8 - m_depth - pos * m_depth;

    unsigned char mask1 = m_mask << shift;
    unsigned int val = (c & mask1) >> shift;

    return val;
  }

};
//! @brief PNMファイルの種類
enum class PNM_TYPE {
  P1 = 1,  //Portable bitmap   ASCII
  P2 = 2,  //Portable graymap  ASCII
  P3 = 3,  //Portable pixmap   ASCII
  P4 = 4,  //Portable bitmap   Binary
  P5 = 5,  //Portable graymap  Binary
  P6 = 6,  //Portable pixmap   Binary
  PError = 7,
  PBitmap = 8,
  PGraymap = 9,
  PPixmap = 10
};
//! @brief ストリームから入力 bitmap ASCII
//! @param [out] img 画像の格納先
//! @param [out] s 入力ストリーム
void read_P1(std::vector<unsigned char>& img, std::ifstream& s) {
  while (s.eof() == false) {

    char c;
    s.read(&c, 1);
    if (isdigit(c) != false) {
      img.push_back(c - '0');
    }

  }

}
//! @brief ストリームから入力 graymap,pixmap ASCII
//! @param [out] img 画像の格納先
//! @param [out] s 入力ストリーム
void read_P23(std::vector<unsigned char> & img, std::ifstream & s) {
  while (s.eof() == false) {

    std::string val;

    s >> val;

    int value;
    try {
      value = std::stoi(val);
    }
    catch (std::invalid_argument) {
      continue;
    }

    img.push_back(value);

  }

}
//! @brief ストリームから入力 bitmap Binary
//! @param [out] img 画像の格納先
//! @param [out] s 入力ストリーム
//! @return なし
//! @details P4は1ビット=1画素なので全部読み込んでから1ビットずつとりだしている
void read_P4(std::vector<unsigned char> & img, std::ifstream & s, int count) {
  std::vector<unsigned char> tmp;

  unsigned char tc;
  while (!s.eof()) {
    s.read((char*)& tc, sizeof(char));
    tmp.push_back(tc);
  }

  CGetGrayBits bits(&tmp[0]);

  for (size_t i = 0; i < count; i++) {
    img.push_back(bits[i]);
  }

}
//! @brief ストリームから入力 graymap,pixmap Binary
//! @param [out] img 画像の格納先
//! @param [out] s 入力ストリーム
//! @return なし
void read_P56(std::vector<unsigned char> & img, std::ifstream & s) {
  while (s.eof() == false) {

    unsigned char c;
    s.read((char*)& c, 1);
    img.push_back(c);

  }
}
//! @brief pnmファイルを読み込む
//! @param [in] fpathname pnmファイルへのパス
//! @param [out] img 画像の格納先
//! @param [out] width 横方向の画素数
//! @param [out] height 縦方向の画素数
//! @param [out] maxvalue 画素の最大値
//! @param [out] datatype 形式を識別する定数
//! @retval true 読込成功
//! @retval false 読込失敗
bool read_pnm(const char* fpathname, std::vector<unsigned char> * img, int* width, int* height, int* maxvalue, PNM_TYPE * datatype) {
  std::ifstream ifs(fpathname, std::ios::in | std::ios::binary);

  if (!ifs)
    return false;

  std::string text;

  *width = -1;
  *height = -1;
  *maxvalue = -1;

  //タイプの読み取り
  PNM_TYPE type;
  {
    int _type = (int)PNM_TYPE::PError;
    while (std::getline(ifs, text).eof() == false) {
      if (text[0] == 'P') {
        _type = ((int)text[1] - (int)'0');
        break;
      }
    }
    switch (_type) {
    case (int)PNM_TYPE::P1:*datatype = PNM_TYPE::PBitmap; break;
    case (int)PNM_TYPE::P2:*datatype = PNM_TYPE::PGraymap; break;
    case (int)PNM_TYPE::P3:*datatype = PNM_TYPE::PPixmap; break;
    case (int)PNM_TYPE::P4:*datatype = PNM_TYPE::PBitmap; break;
    case (int)PNM_TYPE::P5:*datatype = PNM_TYPE::PGraymap; break;
    case (int)PNM_TYPE::P6:*datatype = PNM_TYPE::PPixmap; break;
    default:
      *datatype = PNM_TYPE::PError;
      return false;
    }
    type = (PNM_TYPE)_type;
  }

  while (std::getline(ifs, text).eof() == false) {

    //コメントスキップ
    if (text[0] == '#')
      continue;

    //コメントでないなら各種値の入力
    std::stringstream ss(text);

    //幅入力
    if (*width < 0) {
      ss >> *width;
      if (*width < 0)
        continue;
    }

    //高さ入力
    if (*height < 0) {
      ss >> *height;
      if (*height < 0)
        continue;
    }

    //最大値入力
    if (*datatype != PNM_TYPE::PBitmap) {
      if (*maxvalue < 0) {
        ss >> *maxvalue;
        if (*maxvalue < 0)
          continue;
      }
    }

    break;
  }

  int count = *width * *height;

  switch (type) {
  case PNM_TYPE::P1:
    img->reserve(count);
    read_P1(*img, ifs);
    break;
  case PNM_TYPE::P2:
    img->reserve(count);
    read_P23(*img, ifs);
    break;
  case PNM_TYPE::P3:
    img->reserve(count * 3);
    read_P23(*img, ifs);
    break;
  case PNM_TYPE::P4:
    read_P4(*img, ifs, count);
    break;
  case PNM_TYPE::P5:
    read_P56(*img, ifs);
    break;
  case PNM_TYPE::P6:
    read_P56(*img, ifs);
    break;
  }

}

テスト用コード

#include <iostream>

#include "pnm_reader.hpp"

//! @brief pnmの各型式のデータをppm形式の格納方法に変更する
//! @param [out] dst 結果画像データ
//! @param [in] src 元画像データ
//! @param [in] pixelcount 画素数
//! @param [in] type P1~P6のいずれか
//! @return なし
void pnm_to_ppm(
  std::vector<unsigned char>& dst,
  unsigned char* src,
  int pixelcount,
  PNM_TYPE type
  ) {

  switch (type)
  {
  case PNM_TYPE::P1:
  case PNM_TYPE::P4:
  case PNM_TYPE::PBitmap:
    for (size_t i = 0; i < pixelcount; i++) {
      dst.push_back(src[i] ? 0 : 255);
      dst.push_back(src[i] ? 0 : 255);
      dst.push_back(src[i] ? 0 : 255);
    }
    break;
  case PNM_TYPE::P2:
  case PNM_TYPE::P5:
  case PNM_TYPE::PGraymap:
    for (size_t i = 0; i < pixelcount; i++) {
      dst.push_back(src[i]);
      dst.push_back(src[i]);
      dst.push_back(src[i]);
    }
    break;
  case PNM_TYPE::P3:
  case PNM_TYPE::P6:
  case PNM_TYPE::PPixmap:
    for (size_t i = 0; i < pixelcount; i++) {
      dst.push_back(src[i * 3 + 0]);
      dst.push_back(src[i * 3 + 1]);
      dst.push_back(src[i * 3 + 2]);
    }
    break;
  }
}
//! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む
//! @param [in] fname ファイル名
//! @param [in] vmax 全てのRGBの中の最大値
//! @param [in] width 画像の幅
//! @param [in] height 画像の高さ
//! @param [in] p 画像のメモリへのアドレス
//! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む
void pnmP3_Write(
  const char* const fname, 
  const int vmax, 
  const int width, 
  const int height, 
  const unsigned char* const p) {

  FILE* fp = fopen(fname, "wb");
  fprintf(fp, "P3\n%d %d\n%d\n", width, height, vmax);

  size_t k = 0;
  for (size_t i = 0; i < (size_t)height; i++) {
    for (size_t j = 0; j < (size_t)width; j++) {
      fprintf(fp, "%d %d %d ", p[k * 3 + 0], p[k * 3 + 1], p[k * 3 + 2]);
      k++;
    }
    fprintf(fp, "\n");
  }

  fclose(fp);
}
int main()
{
  const char* fpathname = "C:\\test\\butterfly-5546907_640-binary.ppm";


  std::vector<unsigned char> img;
  int width, height, maxvalue;
  PNM_TYPE type;
  read_pnm(
    fpathname,
    &img,
    &width,
    &height,
    &maxvalue,
    &type
  );

  std::vector<unsigned char> tmp;
  pnm_to_ppm(tmp, &img[0], width * height,type);
  //////////////////////////////
  const char* fname = "C:\\test\\out.ppm";
  pnmP3_Write(fname, 255, width, height, &tmp[0]);


}

コメントを残す

メールアドレスが公開されることはありません。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: