mieki256's diary



2022/07/17() [n年前の日記]

#1 [prog] stbライブラリを試用

_昨日調べて 存在を知った、C/C++で画像処理をするためのstbライブラリを少しだけ試用。

_nothings/stb: stb single-file public domain libraries for C/C++

動作確認環境は Windows10 x64 21H2 + MSYS2 + MinGW64。g++ 12.1.0。GNU Make 4.3。

今回は画像のロードとセーブを試してみたかったので、stb_image.h と stb_image_write.h をDL。メインとなるソースコードと同じ場所に置いて利用した。

画像ロード。 :

stb_image.h の使用例。画像(.png)を読み込んで、画像サイズと1ピクセルあたりのバイト数を出力する。

_01_loadimage.cpp
// load image with stb_image.h

#include <stdio.h>

#define STB_IMAGE_STATIC
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

int main(int argc, char *argv[])
{
  if (argc != 2)
  {
    printf("Usage\n\t01_loadimage.exe IN.png");
  }

  auto filename = argv[1];

  unsigned char *pixels;
  int width;
  int height;
  int bpp;

  pixels = stbi_load(filename, &width, &height, &bpp, 0);

  printf("Image size %d x %d\n", width, height);
  printf("Image bpp %d\n", bpp);

  stbi_image_free(pixels);
}

コンパイルは以下。ここでは静的リンク(スタティックリンク)をしている。
g++ 01_loadimage.cpp -o 01_loadimage.exe -static -lstdc++ -lgcc -lwinpthread

以下で実行。画像サイズと Byte per pixel が出力された。
$ ./01_loadimage.exe in.png
Image size 512 x 512
Image bpp 3

stb_image.h の使い方としては、最初に以下を書いておく。
#define STB_IMAGE_STATIC
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
  • stb_image.h を使う場合、必ず、「#define STB_IMAGE_IMPLEMENTATION」と「#include "stb_image.h"」を記述する。
  • STB_IMAGE_STATIC を定義しておくと、各関数の頭に static がつく、らしい。

  • stbi_load() で画像のロード。
  • 画像を使い終わったら stbi_image_free() で解放する。

画像セーブ。 :

stb_image_write.h の使用例。画像(.png)を読み込んで、グレースケールに変換して、out.png というファイル名で保存する。

_02_writeimage.cpp
// write png image with stb_image_write.h

#include <stdio.h>

#define STB_IMAGE_STATIC
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

#define STB_IMAGE_WRITE_STATIC
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

int main(int argc, char *argv[])
{
  if (argc != 2)
  {
    printf("Usage\n\t02_writeimage.exe IN.png");
  }

  auto filename = argv[1];

  unsigned char *pixels;
  int width;
  int height;
  int bpp;

  // load image
  pixels = stbi_load(filename, &width, &height, &bpp, 0);

  printf("Image size %d x %d\n", width, height);
  printf("Image bpp %d\n", bpp);

  for (int y = 0; y < height; y++)
    for (int x = 0; x < width; x++)
    {
      int idx = (y * width + x) * bpp;

      unsigned int r = (unsigned int)pixels[idx + 0];
      unsigned int g = (unsigned int)pixels[idx + 1];
      unsigned int b = (unsigned int)pixels[idx + 2];

      // convert greyscale
      if (1)
      {
        double grey = 0.299 * (double)r + 0.587 * (double)g + 0.114 * (double)b;
        if (grey < 0)
          grey = 0;
        if (grey > 255)
          grey = 255;
        unsigned int ui_grey = (unsigned int)grey;

        pixels[idx + 0] = ui_grey;
        pixels[idx + 1] = ui_grey;
        pixels[idx + 2] = ui_grey;
      }
    }

  // write image
  if (stbi_write_png("out.png", width, height, 3, pixels, width * 3))
  {
    printf("Save out.png\n");
  }
  else
  {
    printf("Failure save.\n");
  }

  stbi_image_free(pixels);
}

コンパイル。
g++ 02_writeimage.cpp -o 02_writeimage.exe -static -lstdc++ -lgcc -lwinpthread

実行。
$ ./02_writeimage.exe in.png
Image size 512 x 512
Image bpp 3
Save out.png

グレースケールの画像が得られた。

stb_image_write.h の使い方としては、最初に以下を書いておく。
#define STB_IMAGE_WRITE_STATIC
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
  • stb_image_write.h を使う場合、必ず、「#define STB_IMAGE_WRITE_IMPLEMENTATION」と「#include "stb_image_write.h"」を記述する。
  • STB_IMAGE_WRITE_STATIC を定義しておくと、各関数の頭に static がつく、らしい。

  • stbi_write_png() で、png画像の保存。
与える引数は…ちょっとよく分からない。たぶん以下なのかなと思うけど。
stbi_write_png(保存ファイル名, 画像横幅, 画像縦幅, bpp, ピクセルデータ群へのポインタ, 1ライン分のバイト数)
ただ、巷のサンプルを見ると、一番最後で「0」を指定してる場合が多くて…。

ちなみに、stb_image_write.h 内のコメントによると、ソースコードの量を減らすためにpng画像の圧縮率は低い状態になっているそうで。zlib関係の関数を STBIW_ZLIB_COMPRESS に定義することで圧縮率は改善できると書いてあった。もちろん、zlib を使えるようにビルドしないといかんのだろうけど。

何にせよ、stb_image.h にしろ、stb_image_write.h にしろ、比較的簡単な記述で画像のロードやセーブができる上に、静的リンクもすんなりできてしまうとは…。これは便利だなと…。

stbi_write_png() の最後の引数について。 :

stbi_write_png() の最後の引数がよく分からなかったけど、ソースを眺めた感じでは 0 を指定しても良かったらしい。

stbi_write_png() は、内部で stbi_write_png_to_mem() を呼んでいるけれど、この stbi_write_png_to_mem() の中で、値が 0 なら width * bpp を代入、みたいなことをしている。

なので、自分で指定してもいいし、面倒臭ければ 0 を指定してもよい。どちらも同じ結果になるはず。たぶん。

以上、1 日分です。

過去ログ表示

Prev - 2022/07 - Next
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project