スポンサーリンク

C++でSobelフィルタを実装

3×3のソーベルフィルタを実装する。

SobelFilter.hpp
#pragma once


#include <valarray>
#include <array>
 
//! @brief ソーベルフィルタ
//! @note フィルタを変えれば他の用途にも使える
class SobelFilter {

  // フィルタサイズ 3x3
  static constexpr int FilterSize = 3;

  
  std::array < std::array<int, FilterSize>, FilterSize > filterH; //!< 水平方向
  std::array < std::array<int, FilterSize>, FilterSize > filterV; //!< 垂直方向

  int m_width;  //!< 入出力画像の幅
  int m_height; //!< 入出力画像の高さ
  const unsigned char* m_pimg; //!< 元画像へのポインタ


  // -1 →(+1)→ 0 
  //  0 →(+1)→ 1  
  //  1 →(+1)→ 2

  //! @brief 水平のフィルタを取得
  int fH(const int x, const int y)const {
    return filterH[x + 1][y + 1];
  }
  //! @brief  垂直のフィルタを取得
  int fV(const int x, const int y)const {
    return filterV[x + 1][y + 1];
  }

public:
  //! @brief 一画素のサイズ。RGBで3バイト
  static constexpr int PixelSize = 3;

  using Vector3d = std::valarray<int>;

  //! @brief [in] w 画像幅
  //! @brief [in] h 画像幅
  //! @brief [in] pimg 元画像へのポインタ
  SobelFilter(int w, int h, const unsigned char* pimg) {

    //ソーベルフィルタ作成
    filterH[0] = { -1, 0, 1 };
    filterH[1] = { -2, 0, 2 };
    filterH[2] = { -1, 0, 1 };
    filterV[0] = { -1,-2,-1 };
    filterV[1] = { 0, 0, 0 };
    filterV[2] = { 1, 2, 1 };

    // 画像へアクセスする諸々の設定
    m_width = w;
    m_height = h;
    m_pimg = pimg;

  }


  //! @brief 元画像の(x,y)座標の画素を取得
  Vector3d pixel(const int x, const int y) {
    const unsigned char* p = &m_pimg[(y * m_width + x) * PixelSize];

    return Vector3d({ p[0], p[1], p[2] });
  }

  // (x,y)座標のフィルタ後の値を取得
  Vector3d get(const int x, const int y) {
    Vector3d tmp({ 0,0,0 });

    std::array<Vector3d, FilterSize * 2> px;;
    std::fill(px.begin(), px.end(), Vector3d{ 0,0,0 });

    //水平9画素についての合計
    px[0] =
      pixel(x - 1, y - 1) * fH(-1, -1) +
      pixel(x  , y - 1) * fH( 0, -1) +
      pixel(x + 1, y - 1) * fH( 1, -1);

    px[1] =
      pixel(x - 1, y) * fH(-1, 0) +
      pixel(x  , y) * fH( 0, 0) +
      pixel(x + 1, y) * fH( 1, 0);

    px[2] =
      pixel(x - 1, y + 1) * fH(-1, 1) +
      pixel(x  , y + 1) * fH( 0, 1) +
      pixel(x + 1, y + 1) * fH( 1, 1);

    ////////////////////////////////////////////////
    // 垂直9画素についての合計
    px[3] =
      pixel(x - 1, y - 1) * fV(-1, -1) +
      pixel(x, y - 1) * fV(0, -1) +
      pixel(x + 1, y - 1) * fV(1, -1);

    px[4] =
      pixel(x - 1, y) * fV(-1, 0) +
      pixel(x, y) * fV(0, 0) +
      pixel(x + 1, y) * fV(1, 0);

    px[5] =
      pixel(x - 1, y + 1) * fV(-1, 1) +
      pixel(x, y + 1) * fV(0, 1) +
      pixel(x + 1, y + 1) * fV(1, 1);

    for (auto& k : px) {
      for (int i = 0; i < PixelSize; i++) {
        tmp[i] += k[i];
      }
    }

    for (int i = 0; i < PixelSize; i++) {
      tmp[i] = (tmp[i] < 0) ? 0 : tmp[i];
      tmp[i] = (tmp[i] > 255) ? 255 : tmp[i];
    }


    return tmp;

  }
};

      
//! @brief width 画像横画素数
//! @brief height 画像縦画素数
//! @param [in] pimg 入力画像
//! @param [out] dst 出力先
//! @return なし
void sobel(
  const int width,
  const int height,
  const unsigned char* pimg,
  unsigned char* dst
) {


  SobelFilter sf(width, height, pimg);

  size_t sz = SobelFilter::PixelSize;

  for (int x = 1; x < width - 1; x++) {
    for (int y = 1; y < height - 1; y++) {

      SobelFilter::Vector3d px = sf.get(x, y);

      for (size_t p = 0; p < sz; p++) {
        dst[(y * width + x) * sz + p] = px[p];
      }
    }

  }
}

呼び出し例

#include <iostream>

#include <vector>

#include "SobelFilter.hpp"

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

#include "ppmP3_read.hpp" // https://www.study.suzulang.com/cppppm-readerwriter/ppm-p3-reader-cpp

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


int main()
{
  int width;
  int height;
  int vmax;

  unsigned char* pimg;
  ppmP3_read("C:\\data\\b.ppm", &width, &height, &vmax, &pimg);

  std::vector<unsigned char> ret(width * height * 3);
  sobel(width, height, pimg, ret.data());

  ppmP3_write("C:\\data\\sobel.ppm", width, height, ret.data(), 255);


  std::cout << "Hello World!\n";
}
//! @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: