スポンサーリンク

PNM Writer(改)

PNMの書き込み関数を書いた。

ただし前回のPNM Readerで書いたPNM_TYPE列挙体が共通のため、前回のPNM Readerと同じファイルに書くことが望ましい。

プログラム本体(含 PNM Reader)

#pragma once

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

//! @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
};
//////////////////////////////////////////////////////////
// PNM Reader

//! @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 ストリームから入力 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; } }
//
//////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////
// PNM Writer

//! @brief 1byteの各ビットに配列のようにアクセスするためのクラス
class bititerator_t {
  unsigned char* m_c;
  int m_index;
public:
  bititerator_t(unsigned char* c, const int index) {
    m_c = c;
    m_index = 7 - index;
  }
  void operator=(const int value) {
    unsigned char mask = (1 << m_index);

    if (value) {
      *m_c |= mask;
    }
    else {
      *m_c &= ~mask;
    }
  }
  operator unsigned char() {
    unsigned char mask = (1 << m_index);

    return (*m_c & mask) ? 1 : 0;
  }


};

//! @brief 1byteの各ビットに配列のようにアクセスするためのクラス
class BitArray {
  unsigned char c;
public:
  BitArray() :c(0) {}
  bititerator_t operator[](const int i) {
    return bititerator_t(&c, i);
  }
  operator unsigned char() {
    return c;
  }
  unsigned char operator=(const unsigned char value) {
    c = value;
    return c;
  }
};
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////


//! @brief データタイプを文字列に変換
std::string to_string(const PNM_TYPE type) {
  switch (type) {
  case PNM_TYPE::P1:return "P1"; break;
  case PNM_TYPE::P2:return "P2"; break;
  case PNM_TYPE::P3:return "P3"; break;
  case PNM_TYPE::P4:return "P4"; break;
  case PNM_TYPE::P5:return "P5"; break;
  case PNM_TYPE::P6:return "P6"; break;
  }
  return "";

}

//! @brief 各データタイプにおける1ピクセルのビット数を求める
int pixel_bit_count(const PNM_TYPE type) {
  switch (type) {
  case PNM_TYPE::P1:return 1;   break;
  case PNM_TYPE::P2:return 8;   break;
  case PNM_TYPE::P3:return 8 * 3; break;
  case PNM_TYPE::P4:return 1;   break;
  case PNM_TYPE::P5:return 8;   break;
  case PNM_TYPE::P6:return 8 * 3; break;
  }
  return -1;

}
//! @brief Portable bitmap   ASCII
//! @param [in,out] s 出力先
//! @param [in] img 1byte1画素の画像データ
//! @param [in] width 幅画素数
//! @param [in] height 高さ画素数
//! @return なし
void write_P1(std::ofstream& s, const unsigned char* img, const int width, const int height) {
  int count = width * height;
  for (size_t i = 0; i < count; i++) {
    s << std::to_string(img[i] ? 1 : 0);
  }
}
//! @brief ストリームへ出力 Portable graymap  ASCII
//! @param [in,out] 出力先
//! @param [in] img 画像データ
//! @param [in] width 画素数幅
//! @param [in] height 画素数高さ
//! @return なし
void write_P2(std::ofstream & s, const unsigned char* img, const int width, const int height) {
  int count = width * height;
  int i = 0;
  for (size_t h = 0; h < height; h++) {
    for (size_t w = 0; w < width; w++) {
      s << std::to_string(img[i]) << " ";
      i++;
    }
    s << std::endl;
  }
}
//! @brief ストリームへ出力 Portable pixmap   ASCII
//! @param [in,out] 出力先
//! @param [in] img 画像データ
//! @param [in] width 画素数幅
//! @param [in] height 画素数高さ
//! @return なし
void write_P3(std::ofstream & s, const unsigned char* img, const int width, const int height) {
  int count = width * height;
  int i = 0;
  for (size_t h = 0; h < height; h++) {
    for (size_t w = 0; w < width; w++) {
      s << std::to_string(img[i * 3 + 0]) << " ";
      s << std::to_string(img[i * 3 + 1]) << " ";
      s << std::to_string(img[i * 3 + 2]) << " ";
      i++;
    }
    s << std::endl;
  }
}
//! @brief ストリームへ出力 Portable bitmap   Binary
//@details 入力が1byte 1pixel なので、 8要素を1byteにまとめる
//! @param [in,out] 出力先
//! @param [in] img 画像データ
//! @param [in] count 画素数
//! @return なし
void write_P4(std::ofstream & s, const unsigned char* img, const int count) {

  int bitindex = 0;
  BitArray bits;
  for (size_t i = 0; i < count; i++) {

    if (bitindex == 0) {
      bits = 0;
    }

    bits[bitindex] = img[i];

    bitindex++;
    bitindex %= 8;

    if (bitindex == 0) {
      unsigned char c = bits;
      s.write((const char*)& c, 1);
    }
  }

}
//! @brief ストリームへ出力 Portable graymap  Binary , Portable pixmap   Binary
//@details 入力が1byte 1pixel なので、 8要素を1byteにまとめる
//! @param [in,out] 出力先
//! @param [in] img 画像データ
//! @param [in] bytes 画像データのバイト数
//! @return なし
void write_P56(std::ofstream & s, const unsigned char* img, const int bytes) {
  s.write((char*)img, bytes);
}
//! @brief pnmファイルを書き込む
//! @param [in] fpathname pnmファイルへのパス
//! @param [in] img 画像の格納先
//! @param [int] width 横方向の画素数
//! @param [in] height 縦方向の画素数
//! @param [in] datatype 形式を識別、指定する定数
//! @retval true 書き込み成功
//! @retval false 書き込み失敗
bool write_pnm(const char* fpathname, const unsigned char* img, const int width, const int height, const PNM_TYPE datatype) {

  if (datatype >= PNM_TYPE::PError)
    return false;

  int count = width * height;
  if (count <= 0)
    return false;

  std::ofstream ofs(fpathname, std::ios::out | std::ios::binary);
  if (!ofs)
    return false;


  int pixelbit = pixel_bit_count(datatype);


  ofs << to_string(datatype) << std::endl;
  ofs << width << " " << height << std::endl;

  int maxvalue = -1;
  if ((datatype != PNM_TYPE::P1) && (datatype != PNM_TYPE::P4)) {
    maxvalue = *std::max_element(
      img,
      img + count * pixel_bit_count(datatype) / 8);

    ofs << maxvalue << std::endl;
  }


  switch (datatype) {
  case PNM_TYPE::P1:
    write_P1(ofs, img, width, height);
    break;
  case PNM_TYPE::P2:
    write_P2(ofs, img, width, height);
    break;
  case PNM_TYPE::P3:
    write_P3(ofs, img, width, height);
    break;
  case PNM_TYPE::P4:
    write_P4(ofs, img, width * height);
    break;
  case PNM_TYPE::P5:
    write_P56(ofs, img, width * height);
    break;
  case PNM_TYPE::P6:
    write_P56(ofs, img, width * height * 3);
    break;
  }
}

テストプログラム

#include <iostream>

#include "pnm_rw.hpp"


int main()
{
  const char* fpathname = "C:\\test\\butterfly-5546907_640-ascii.pbm";


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

  switch (type) {

  case PNM_TYPE::PBitmap:
    write_pnm("C:\\test\\out-ascii.pbm", img.data(), width, height, PNM_TYPE::P1);
    write_pnm("C:\\test\\out-binary.pbm", img.data(), width, height, PNM_TYPE::P4);
    break;
  case PNM_TYPE::PGraymap:
    write_pnm("C:\\test\\out-ascii.pgm", img.data(), width, height, PNM_TYPE::P2);
    write_pnm("C:\\test\\out-binary.pgm", img.data(), width, height, PNM_TYPE::P5);
    break;
  case PNM_TYPE::PPixmap:
    write_pnm("C:\\test\\out-ascii.ppm", img.data(), width, height, PNM_TYPE::P3);
    write_pnm("C:\\test\\out-binary.ppm", img.data(), width, height, PNM_TYPE::P6);
    break;
  }
}

コメントを残す

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

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


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