ぬの部屋(仮)
nu-no-he-ya
  •    1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
         12
    3456789
    10111213141516
    17181920212223
    242526272829 
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
        123
    45678910
    11121314151617
    18192021222324
    25262728   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    15161718192021
    293031    
           
         12
    3456789
    10111213141516
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
          1
    2345678
    9101112131415
    16171819202122
    232425262728 
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
  • bit depth が非8の倍数ビットの時に画素値を取得する

    libpngで8bit,16bit以外のビット深度のグレイスケール画像を読み込むと、メモリ上には1bit,2bit,4bitなどごとにデータが格納されているので、1byteごとデータを取り出すような簡単な処理で取得できない。

    ちょっと調べた限りでは画素値を素直に取り出す関数などが見当たらなかった(あるかもしれない)のでとりあえず適当に作ることにした。

    動作

    例えば、bit depth==4で

    10101011 , 01001110 , 10000101 というデータがあったら、

    1010,1011,0100,1110,1000,0101

    と分割する

    原理

    対象が1bit,2bit,4bitの時は、以下のような感じで、該当部分のビット列だけを抜き出して右にシフトする。

    対象が8bitの時はデータをそのままunsigned char型の配列として扱う。

    対象が16bitの時はデータをそのままunsigned short型の配列として扱い、取得した2byteの前後をswapする。

    //! @file CGetGrayBits.hpp
    //! @brief 1bit,2bit,4bit,8bit,16bitごとに納められたデータを取得する
    
    #pragma once
    
    
    //! @class CGetGrayBits
    //! @brief 配列上のx番目の画素値を取得するクラス
    class CGetGrayBits {
      unsigned char* m_p;        //!< 画素へのポインタ
      int m_depth;               //!< ビット深度
    
      unsigned char m_mask = 0;  //!< depth8未満の時のビットマスク
    public:
    
    
      //! @brief コンストラクタ
      //! @param [in] p 画素配列へのポインタ
      //! @param [in] depth ビット深度。1画素が何ビットで表現されているか
      CGetGrayBits(
        const void* const p,
        const int depth) :
        m_p(static_cast<unsigned char*>(const_cast<void*>(p))),
        m_depth(depth) {
    
        //ビット深度が8未満の時に使用するマスクを作成
        if (m_depth < 8) {
          for (int i = 0; i < m_depth; i++)
            m_mask = (m_mask << 1) | 1;
        }
    
      }
      //! @brief 指定した番号の画素を取得
      //! @param [in] x 画素番号
      //! @return 画素値
      int operator[](const std::size_t x) {
        switch (m_depth) {
        case 1:
        case 2:
        case 4:
          return getgrayLess8(x);
          break;
        case 8:
          return getgray8(x);
          break;
        case 16:
          return getgray16(x);
          break;
        }
        return -1;
      }
      //! @brief 指定した番号の画素を取得(ビット深度==8の時)
      //! @param [in] x 画素番号
      //! @return 画素値をintで表現したもの
      inline int getgray8(const std::size_t x)const {
        return static_cast<int>(m_p[x]);
      }
    
      //! @brief 指定した番号の画素を取得(ビット深度==16の時)
      //! @param [in] x 画素番号
      //! @return 画素値をintで表現したもの
      inline int getgray16(const std::size_t x)const {
        unsigned short t = reinterpret_cast<unsigned short*>(m_p)[x];
        std::swap(
          reinterpret_cast<unsigned char*>(&t)[0],
          reinterpret_cast<unsigned char*>(&t)[1]);
    
        return t;
    
      }
    
      //! @brief 指定した番号の画素を取得(ビット深度<8の時)
      //! @param [in] x 画素番号
      //! @return 画素値をintで表現したもの
      inline int getgrayLess8(const std::size_t x)const {
        // 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;
      }
    };
    

    使用例

    #pragma once
    
    #include <cstdlib>
    #include <iostream>
    #include <vector>
    #include <bitset>
    
    #include <png.h>
    
    #include <algorithm>
    
    #include "CGetGrayBits.hpp"
    
    #pragma comment(lib,"libpng16.lib")
    
    void out_to_ppm(
      const char* outpath, 
      const int width, 
      const int height, 
      const std::vector<int>& pixels
    );
    
    
    
    //エラーの時強制終了
    void abort_(const char* c) {
      printf(c);
      abort();
    }
    
    //! @brief pngファイル読み込み関数
    //! @param [in] file_name ファイル名
    //! @param [out] width 画像幅(ピクセル)
    //! @param [out] height 画像高さ(ピクセル)
    //! @param [out] color_type RGBかRGBAか...等
    //! @param [out] bit_depth チャンネルのビット数
    //! @param [out] row_pointers 画像データへのポインタのポインタ
    //! @param [out] palette パレット
    //! @param [out] palette_num パレット色の個数
    void read_png(
      const char* file_name,
      int* width,
      int* height,
      png_byte* color_type,
      png_byte* bit_depth,
      png_bytep** row_pointers,
      png_colorp* palette,
      int* palette_num
    ) {
    
      png_byte  header[8];    // 8 is the maximum size that can be checked
    
      FILE* fp = fopen(file_name, "rb");
      if (!fp) {
        abort_("[read_png_file] File could not be opened for reading");
      }
      fread(header, 1, 8, fp);
      if (png_sig_cmp(header, 0, 8)) {
        abort_("[read_png_file] File is not recognized as a PNG file");
      }
    
    
      png_structp png_ptr;
      png_infop info_ptr;
    
      /* initialize stuff */
      png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    
      if (!png_ptr)
        abort_("[read_png_file] png_create_read_struct failed");
    
      info_ptr = png_create_info_struct(png_ptr);
      if (!info_ptr)
        abort_("[read_png_file] png_create_info_struct failed");
    
      if (setjmp(png_jmpbuf(png_ptr)))
        abort_("[read_png_file] Error during init_io");
    
      png_init_io(png_ptr, fp);
      png_set_sig_bytes(png_ptr, 8);
    
      png_read_info(png_ptr, info_ptr);
    
      /////////////////////////////////////////
      // 画像情報の取得
      *width = png_get_image_width(png_ptr, info_ptr);
      *height = png_get_image_height(png_ptr, info_ptr);
      *color_type = png_get_color_type(png_ptr, info_ptr);
      *bit_depth = png_get_bit_depth(png_ptr, info_ptr);
      //
      /////////////////////////////////////////
    
      /////////////////////////////////////////
      // パレットの取得
      if (*color_type & PNG_COLOR_TYPE_PALETTE) {
        png_get_PLTE(png_ptr, info_ptr, palette, palette_num);
      }
      //
      /////////////////////////////////////////
    
      int number_of_passes;
      number_of_passes = png_set_interlace_handling(png_ptr);
      png_read_update_info(png_ptr, info_ptr);
    
      /* read file */
      if (setjmp(png_jmpbuf(png_ptr)))
        abort_("[read_png_file] Error during read_image");
    
      /////////////////////////////////////////
      // 画像の読み込み
      *row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * *height);
    
      for (int y = 0; y < *height; y++)
        (*row_pointers)[y] = (png_byte*)malloc(png_get_rowbytes(png_ptr, info_ptr));
    
      png_read_image(png_ptr, *row_pointers);
      //
      /////////////////////////////////////////
    
    
      fclose(fp);
    }
    int main()
    {
      int width;
      int height;
      png_byte color_type;
      png_byte bit_depth;
      png_bytep* row_pointers;
      png_colorp palette;
      int palette_num;
    
      ////////////////////////////////
      // 画像読み込み
      read_png(
        R"(c:\test\data\snail2.png)",
        &width,
        &height,
        &color_type,
        &bit_depth,
        &row_pointers,
        &palette,
        &palette_num
      );
      //
      ////////////////////////////////
    
      std::cout << "width : " << width << std::endl;
      std::cout << "height: " << height << std::endl;
      std::cout << "colortype: " << (int)color_type << std::endl;
      std::cout << "bitdepth: " << (int)bit_depth << std::endl;
    
      ////////////////////////////////
      //グレイスケール画像だけを対象とする
      if (color_type != (PNG_COLOR_TYPE_GRAY )){
        std::cout << "not support" << std::endl;
        int i;
        std::cin >> i;
        return -1;
      }
      //
      ////////////////////////////////
    
    
      ////////////////////////////////
      // 内容を取得
      std::vector<int> imgvec;
      for (int y = 0; y < height; y++) {
        png_bytep yhead = row_pointers[y]; // 画像の横一列の先頭を取得
    
        CGetGrayBits g(yhead, bit_depth); // 画素取得の準備
    
        for (int x = 0; x < width; x++) {
    
          int index = g[x]; // x番目の画素値をintで取得
    
          imgvec.push_back( index ); // 画素値を保存
        }
    
      }
      //
      ///////////////////////////////////////////////
      ///////////////////////////////////////////////
      // 画像として保存
      char outpath[1024];
      sprintf(outpath, R"(C:\test\out-%d-palette.ppm)",bit_depth);
      out_to_ppm(outpath, width, height, imgvec);
      //
      ///////////////////////////////////////////////
    
    
      ///////////////////////////////////////////////
      // メモリの解放
      for (size_t i = 0; i < (size_t)height; i++) {
        png_bytep yhead = row_pointers[i];
        free(yhead);
      }
      free(row_pointers);
      //
      ///////////////////////////////////////////////
    
      int i;
      std::cin >> i;
    
    }
    
    
    void out_to_ppm(const char* outpath,const int width, const int height, const std::vector<int>& pixels) {
    
      int pixmax = *std::max_element(pixels.begin(), pixels.end());
    
      FILE* fp = fopen(outpath, "wb");
      fprintf(fp, "P3\n%d %d\n%d\n", width, height, 255);
    
      for (size_t y = 0; y < (size_t)height; y++) {
    
        for (size_t x = 0; x < (size_t)width; x++) {
    
          int index = y * width + x;
    
          unsigned char c = pixels[index] / (double)pixmax * 255;
    
          fprintf(fp, "%d %d %d ", c, c, c);
    
        }
        fprintf(fp, "\n");
      }
    
      fclose(fp);
    }
    

    テストデータ

    前回ImageMagickで作成したデータを用いる。