mieki256's diary



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

#1 [prog] FreeImageとDevILライブラリを試用

C/C++で、pngファイルを読み込んだり書き込んだりする処理をしてみたい。

画像処理用のライブラリとして、FreeImage、DevIL というライブラリがあるらしいと知ったので試用してみた。

_C/C++ にて 画像ファイルを読み込む - Qiita


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

FreeImageライブラリ。 :

C/C++ から利用できる画像処理用ライブラリとして、FreeImage というライブラリがあるらしい。

_The FreeImage Project


ライセンスは、GPL、または FreeImage Public License (FIPL)。どちらかを選べる、ということなのだろう。たぶん。

_GNU General Public License | Open Source Initiative
_freeimage.sourceforge.net/freeimage-license.txt


MSYS2 なら、パッケージが既に用意されていた。pacman -Ss hoge でパッケージ検索。
$ pacman -Ss freeimage
mingw32/mingw-w64-i686-freeimage 3.18.0-8
    Library project for developers who would like to  support popular graphics image formats (mingw-w64)
mingw64/mingw-w64-x86_64-freeimage 3.18.0-8
    Library project for developers who would like to  support popular graphics image formats (mingw-w64)
ucrt64/mingw-w64-ucrt-x86_64-freeimage 3.18.0-8
    Library project for developers who would like to  support popular graphics image formats (mingw-w64)
clang32/mingw-w64-clang-i686-freeimage 3.18.0-8
    Library project for developers who would like to  support popular graphics image formats (mingw-w64)
clang64/mingw-w64-clang-x86_64-freeimage 3.18.0-8
    Library project for developers who would like to  support popular graphics image formats (mingw-w64)

インストールしてみる。pacman -S hoge でインストールできる。
pacman -S mingw-w64-x86_64-freeimage
pacman -S mingw-w64-i686-freeimage

以下を参考にしてサンプルを用意した。pngファイルを読み込んで、1ドットずつグレースケール変換して、out.png として保存する、という処理をしている。

_FreeImageで画像の読み込みと保存 - Qiita
_画像変換ツールを自作する - Qiita
_FreeImage 3.13.1 documentation - FreeImage3131.pdf

_01_load_png01.cpp
// load png image with freeimage.

#include <stdio.h>
#include <iostream>
#include <FreeImage.h>

const char *outfile = "out.png";

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

  FreeImage_Initialise(FALSE);

  printf("FreeImage ver. %s\n", FreeImage_GetVersion());
  printf("%s\n\n", FreeImage_GetCopyrightMessage());

  try
  {
    auto filename = argv[1];

    // get input image format
    FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
    int flag = 0;

    fif = FreeImage_GetFileType(filename);
    if (fif == FIF_UNKNOWN)
      fif = FreeImage_GetFIFFromFilename(filename);

    if (fif == FIF_PNG)
      flag = PNG_DEFAULT;
    else if (fif == FIF_BMP)
      flag = BMP_DEFAULT;
    else if (fif == FIF_GIF)
      flag = GIF_DEFAULT;
    else if (fif == FIF_JPEG)
      flag = JPEG_DEFAULT;

    // load image
    FIBITMAP *image = FreeImage_Load(fif, filename, flag);
    if (image)
      printf("Load : %s\n", filename);
    else
      throw std::runtime_error("Load failed");

    // convert to 24bit image
    FIBITMAP *image24 = FreeImage_ConvertTo24Bits(image);

    // convert greyscale
    int width = FreeImage_GetWidth(image24);
    int height = FreeImage_GetHeight(image24);
    FIBITMAP *newimg = FreeImage_Allocate(width, height, 24);
    RGBQUAD col;

    for (int y = 0; y < height; y++)
    {
      for (int x = 0; x < width; x++)
      {
        FreeImage_GetPixelColor(image24, x, y, &col);

        double ncol = 0.299 * col.rgbRed + 0.587 * col.rgbGreen + 0.114 * col.rgbBlue;
        if (ncol < 0)
          ncol = 0;
        if (ncol > 255)
          ncol = 255;
        col.rgbRed = int(ncol);
        col.rgbGreen = int(ncol);
        col.rgbBlue = int(ncol);
        
        FreeImage_SetPixelColor(newimg, x, y, &col);
      }
    }

    // save png image
    if (FreeImage_Save(FIF_PNG, newimg, outfile, PNG_DEFAULT))
      printf("Save : %s\n", outfile);
    else
      throw std::runtime_error("Save failed");

    FreeImage_Unload(image);
    FreeImage_Unload(newimg);
  }
  catch (std::exception &e)
  {
    std::cout << "exception!\n"
              << e.what() << std::endl;
  }

  FreeImage_DeInitialise();

  return 0;
}

コンパイルは以下。
g++ 01_load_png01.cpp -o 01_load_png01.exe -lfreeimage

実行。
./01_load_png01.exe in.png

グレースケールになった out.png が得られた。

ただ、ldd 01_load_png01.exe と打って、使用している .dll を確認したところ、かなり多い。ここまで多いのでは、おそらく静的リンク(スタティックリンク)はできない予感がする。

DevILライブラリ。 :

C/C++から利用できる画像処理用ライブラリとして、DevILというライブラリがあるらしい。

_DevIL - A full featured cross-platform Image Library
_DevILを用いた画像ファイルの読み込み - 月とスカラベ
_How To Load and Display an Image with DevIL and OpenGL | Geeks3D

MSYS2では、FreeImage と同様にパッケージが用意されていた。
$ pacman -Ss devil
mingw32/mingw-w64-i686-devil 1.8.0-8
    Library for reading several different image formats  (mingw-w64)
mingw64/mingw-w64-x86_64-devil 1.8.0-8
    Library for reading several different image formats  (mingw-w64)
ucrt64/mingw-w64-ucrt-x86_64-devil 1.8.0-8
    Library for reading several different image formats  (mingw-w64)
clang32/mingw-w64-clang-i686-devil 1.8.0-8
    Library for reading several different image formats  (mingw-w64)
clang64/mingw-w64-clang-x86_64-devil 1.8.0-8
    Library for reading several different image formats  (mingw-w64)

インストール。
pacman -S mingw-w64-x86_64-devil
pacman -S mingw-w64-i686-devil

あちこちからコピペしてサンプルを書いてみた。画像を読み込んで画像サイズを出力するだけのスクリプト。

_01_load_image_devil.cpp
// DevIL sample.

#include <IL/ilu.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#undef USE_WCHAR_T

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

    char *filename = argv[1];

    ILuint img;
    int width, height;

    // DevIL init
    ilInit();
    iluInit();

    // generate image ID
    ilGenImages(1, &img);

    ilBindImage(img);

#ifdef USE_WCHAR_T
    // char to wchar_t
    size_t newsize = strlen(filename) + 1;
    wchar_t *wcfilename = new wchar_t[newsize];
    size_t convertedChars = 0;
    mbstowcs_s(&convertedChars, wcfilename, newsize, filename, _TRUNCATE);
#endif

    ILboolean fg;

    if (1)
    {
        // not use ilLoadImage()

        ILubyte *lump;
        ILuint size;
        FILE *fp;

        fp = fopen(filename, "rb");
        fseek(fp, 0, SEEK_END);
        size = ftell(fp);

        lump = (ILubyte *)malloc(size);
        fseek(fp, 0, SEEK_SET);
        fread(lump, 1, size, fp);
        fclose(fp);

        fg = ilLoadL(IL_PNG, lump, size);

        free(lump);
    }
    else
    {
        // use ilLoadImage()

        fg = ilLoadImage(filename);
    }

    if (fg)
    {
        // load success
        printf("Load : %s\n", filename);

        width = ilGetInteger(IL_IMAGE_WIDTH);
        height = ilGetInteger(IL_IMAGE_HEIGHT);
        printf("image size : %d x %d\n", width, height);

        if (ilGetInteger(IL_IMAGE_ORIGIN) != IL_ORIGIN_LOWER_LEFT)
        {
            iluFlipImage();
        }
    }
    else
    {
        // load failure
        ILenum err = ilGetError();
        std::cerr << iluErrorString(err) << std::endl;
    }

    ilDeleteImages(1, &img);

#ifdef USE_WCHAR_T
    delete wcfilename;
#endif

    return 0;
}

以下でコンパイル。
g++ 01_load_image_devil.cpp -o 01_load_image_devil.exe -lIL -lILU

一応動いたものの、ldd 01_load_image_devil.exe で使用DLLを確認したら、これまたかなりの .dllを利用すると分かった。

試しに -static をつけて静的リンクを試みたところ、-lIL -ILU が見つからないとエラーが出てしまった。ググった感じでは、静的リンクをするなら DevILライブラリ自体をビルドし直さないといけないらしい。

wchar_t と char の変換。 :

Visual Studio Code 上で、DevIL のサンプルを書いていた際、ilLoadImage() に char* を渡す書き方をしたところ、「この関数は wchar_t* を要求している」と警告が表示されてしまった。

色々ググって、char* を wchar_t* に変換するやり方を記述したのだけど…。MSYS2 + MinGW64 でビルドしてみたら、今度は「これは char* を要求する関数だぞ」と言われてエラーが出てしまった。

どうやら、Visual Studio Code 上の警告は無視して、そのまま char* を渡せば良かったらしい。

ちなみに、char* から wchar_t* に変換する処理は以下。wcfilename に wchar_t の文字列が入る。はず。また、使い終わったら delete で消去(解放)しておかないといけない。
    // char to wchar_t
    size_t newsize = strlen(filename) + 1;
    wchar_t *wcfilename = new wchar_t[newsize];
    size_t convertedChars = 0;
    mbstowcs_s(&convertedChars, wcfilename, newsize, filename, _TRUNCATE);
...
    delete wcfilename;

さておき。前述の問題の解決策が分かった。#define _UNICODE を無効にすれば、wchar_t* ではなく char* が使われる状態になるらしい。そのあたり、以下の韓国語ページで解説されていた。

_c++ devil library char または wchar_t の問題

vscode(Visual Studio Code)上で、この定義、_UNICODE を無効にするためには、以下の解説に従って作業する。

_Visual Studio Code C++ remove 'define' in c_cpp_properties.json - Stack Overflow

まず、vscode-preinclude.h というファイルをワークスペースフォルダ内に作って、以下の一文を書いておく。
#undef _UNICODE

vscode上で、Ctrl + Shift + P → C/C++:構成の編集(UI) C/C++: Edit Configurations (UI)。設定画面が出てくるので下のほうまでスクロールして「詳細設定」をクリック。「強制インクルード」の欄に以下を記述。
${workspaceFolder}/vscode-preinclude.h

これで、vscode に vscode-preinclude.h が読み込まれて、そこに _UNICODE の定義を削除する一文が書いてあるから、DevIL の各関数が、wchar_t* ではなく char* を要求するものとして vscode に認識されて、おかしな警告が表示されなくなる。

あるいは、以下で紹介されているように、C++ の標準機能でファイルを開いてメモリに格納してから、そのメモリ領域を ilLoadL() に渡すのもアリかもしれない。

_The Developer's Image Library (DevIL) Tutorials

#2 [nitijyou] 体調が悪い

朝、妙な息苦しさを感じた。喉にも痛みが。なんだろうコレは…。

実のところ、昨日一昨日から息苦しさを感じる瞬間はあって。寝ようとして横になったら急に息苦しくなったりとか。

症状からして、もしやコロナだろうか…。外出、というか密になる場面はできるだけ少なくしてるつもりなのだけど…。 *1
*1: 夜食を買いに、数日に1回、近所のスーパー(ザ・ビッグ)、もしくはドラッグストア(カワチ)に入るぐらいで。できるだけ素早く買い物して早く店から出るように心掛けてはいるつもり。

以上、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