スポンサーリンク

グレイスケール値をサーモグラフィーのように表示

thermography.h

#pragma once

#include <vector>
#include<cassert>

class Thermography {
  using InputColor = std::array<int, 3>;
  using CalcColor = std::array<double, 3>;
  using OutputColor = std::array<unsigned char, 3>;

  std::vector< InputColor > m_ctable;
  double m_dmin;
  double m_dmax;
private:

  template<typename Return>
  Return clamp(Return _min, Return _max, double value) const {
    if (value < _min)
      return _min;
    if (value > _max)
      return _max;
    return value;
  }

  //! @brief 0.0~1.0 の範囲のRGBを取得
  CalcColor calc(const int from, const double ratio)const {
    constexpr int R = 0, G = 1, B = 2;
    const int to = from + 1;

    double r = (m_ctable[from][R] + (m_ctable[to][R] - m_ctable[from][R]) * ratio) / 255.0;
    double g = (m_ctable[from][G] + (m_ctable[to][G] - m_ctable[from][G]) * ratio) / 255.0;
    double b = (m_ctable[from][B] + (m_ctable[to][B] - m_ctable[from][B]) * ratio) / 255.0;

    return CalcColor{r,g,b};
  }

  //! @brief 0~255の範囲のRGBを取得
  OutputColor toOutput(const CalcColor& cc)const {
    return
      OutputColor{
        clamp<unsigned char>(0 ,255 ,cc[0] * 255),
        clamp<unsigned char>(0 ,255 ,cc[1] * 255),
        clamp<unsigned char>(0 ,255 ,cc[2] * 255)
    };

  }


public:
  // @brief カラーテーブルを設定する
  template<class It>
  void setColorTable(It first, It last) {
    for (auto c = first;c!= last;c++)
      m_ctable.push_back(*c);
  }
  // @brief カラーテーブルを設定する
  void setColorTable(
    std::initializer_list< InputColor > colors
  ) {
    setColorTable(colors.begin(), colors.end());
  }

  // @brief 着色する値の範囲を設定
  void setArea(double _min, double _max) {
    m_dmin = _min;
    m_dmax = _max;
  }

  //! @brief 値をRGB色へ変換
  OutputColor thermography(const double value)const {
    constexpr int R = 0, G = 1, B = 2;

    int N = m_ctable.size();
    // 値の正規化
    double nvalue;
    double vwidth = m_dmax - m_dmin;
    nvalue = (value - m_dmin) / vwidth;

    int from, to;
    double ratio;
    //////////////////////////////
    // 範囲外
    if (nvalue <= 0.0) {
      from = 0;
      ratio = 0.0;
    }
    else if (nvalue >= 1.0) {
      from = m_ctable.size() - 2;
      ratio = 1.0;
    }
    //////////////////////////////
    // 範囲内なら from ~ to の間で正規化してratioにする
    else {
      from = nvalue * (N - 1);
      to = from + 1;

      double NnFrom = from / double(N - 1);
      double NnTo = to / double(N - 1);
      ratio = (nvalue - NnFrom) / (NnTo - NnFrom);
    }

    CalcColor rgb = calc(from, ratio);

    return toOutput(rgb);

  }
};

呼び出し例

#include <iostream>

// https://www.study.suzulang.com/2dcg-functions/nbyte-data-type
#include "NByteData.hpp"

#include "ppmP3_read.hpp"

#include "thermography.h"

#include <vector>
#include <array>

void ppmP3_write(
  const char* const fname,
  const int width,
  const int height,
  const unsigned char* const p,
  const int vmax
);

int main()
{
  using PixelT = NByteData<3>;

  std::vector<PixelT> mem;

  // 画像読み込み
  int width;
  int height;
  int vmax;

  ppmP3_read(
    "C:\\test\\ppp.ppm",
    &width,
    &height,
    &vmax,
    [&mem](const size_t pixelcount) {
    mem.resize(pixelcount);
    return (unsigned char*)&mem[0];
  }
  );


  //////////////////////////////////////////////
  //////////////////////////////////////////////
  std::vector< std::array<int, 3>> ColorTable{
    std::array<int, 3>{ 0 , 0,  0 }, // 黒
    std::array<int, 3>{ 0 , 0, 255}, // 青
    std::array<int, 3>{ 0 ,200,200}, // 水色
    std::array<int, 3>{ 0 ,255, 0 }, // 緑
    std::array<int, 3>{200,200, 0 }, // 橙
    std::array<int, 3>{255, 0 , 0 }, // 赤
    std::array<int, 3>{255,255,255}  // 白
  };

  Thermography c;
  c.setArea(0, 255);//この範囲外の値は0,255として扱われる
  c.setColorTable(ColorTable.begin(), ColorTable.end());
  
  // 色を反転
  for (auto& p : mem) {

    std::array<unsigned char,3> rgb = c.thermography(p.data()[0]);

    p.data()[0] = rgb[0];
    p.data()[1] = rgb[1];
    p.data()[2] = rgb[2];

    p.data()[0] = (p.data()[0] > 255) ? 255 : p.data()[0];
    p.data()[1] = (p.data()[1] > 255) ? 255 : p.data()[1];
    p.data()[2] = (p.data()[2] > 255) ? 255 : p.data()[2];

  }

  /////////////////////////////////
  /////////////////////////////////
  ppmP3_write(
    "C:\\test\\qqq.ppm",
    width,
    height,
    (unsigned char*)&mem[0], 255);

}



//! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む
//! @param [in] fname ファイル名
//! @param [in] width 画像の幅
//! @param [in] height 画像の高さ
//! @param [in] p 画像のメモリへのアドレス
//! @param [in] vmax 全てのRGBの中の最大値。普通の画像なら255
//! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む
void ppmP3_write(
  const char* const fname,
  const int width,
  const int height,
  const unsigned char* const p,
  const int vmax
) {

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

ppmP3_read.hpp

#include <iostream>

// https://www.study.suzulang.com/2dcg-functions/nbyte-data-type
#include "NByteData.hpp"

// https://suzulang.com/ppmp3_read-memalloc-ver/
#include "ppmP3_read.hpp" #include "thermography.h" #include <vector> #include <array> void ppmP3_write( const char* const fname, const int width, const int height, const unsigned char* const p, const int vmax ); int main() { using PixelT = NByteData<3>; std::vector<PixelT> mem; // 画像読み込み int width; int height; int vmax; ppmP3_read( "C:\\test\\ppp.ppm", &width, &height, &vmax, [&mem](const size_t pixelcount) { mem.resize(pixelcount); return (unsigned char*)&mem[0]; } );
  //////////////////////////////////////////////
  //////////////////////////////////////////////
  std::vector< std::array<int, 3>> ColorTable{
    std::array<int, 3>{ 0 , 0,  0 }, // 黒
    std::array<int, 3>{ 0 , 0, 255}, // 青
    std::array<int, 3>{ 0 ,200,200}, // 水色
    std::array<int, 3>{ 0 ,255, 0 }, // 緑
    std::array<int, 3>{200,200, 0 }, // 橙
    std::array<int, 3>{255, 0 , 0 }, // 赤
    std::array<int, 3>{255,255,255}  // 白
  };

  Thermography c;
  c.setArea(0, 255);//この範囲外の値は0,255として扱われる
  c.setColorTable(ColorTable.begin(), ColorTable.end());
  
  // 色を反転
  for (auto& p : mem) {

    std::array<unsigned char,3> rgb = c.thermography(p.data()[0]);

    p.data()[0] = rgb[0];
    p.data()[1] = rgb[1];
    p.data()[2] = rgb[2];

    p.data()[0] = (p.data()[0] > 255) ? 255 : p.data()[0];
    p.data()[1] = (p.data()[1] > 255) ? 255 : p.data()[1];
    p.data()[2] = (p.data()[2] > 255) ? 255 : p.data()[2];

  }

  /////////////////////////////////
  /////////////////////////////////
  ppmP3_write(
    "C:\\test\\qqq.ppm",
    width,
    height,
    (unsigned char*)&mem[0], 255);

}



//! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む
//! @param [in] fname ファイル名
//! @param [in] width 画像の幅
//! @param [in] height 画像の高さ
//! @param [in] p 画像のメモリへのアドレス
//! @param [in] vmax 全てのRGBの中の最大値。普通の画像なら255
//! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む
void ppmP3_write(
  const char* const fname,
  const int width,
  const int height,
  const unsigned char* const p,
  const int vmax
) {

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

コメントを残す

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

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


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