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、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 -S hoge でインストールできる。
以下を参考にしてサンプルを用意した。pngファイルを読み込んで、1ドットずつグレースケール変換して、out.png として保存する、という処理をしている。
_FreeImageで画像の読み込みと保存 - Qiita
_画像変換ツールを自作する - Qiita
_FreeImage 3.13.1 documentation - FreeImage3131.pdf
_01_load_png01.cpp
コンパイルは以下。
実行。
グレースケールになった out.png が得られた。
ただ、ldd 01_load_png01.exe と打って、使用している .dll を確認したところ、かなり多い。ここまで多いのでは、おそらく静的リンク(スタティックリンク)はできない予感がする。
_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 と同様にパッケージが用意されていた。
インストール。
あちこちからコピペしてサンプルを書いてみた。画像を読み込んで画像サイズを出力するだけのスクリプト。
_01_load_image_devil.cpp
以下でコンパイル。
一応動いたものの、ldd 01_load_image_devil.exe で使用DLLを確認したら、これまたかなりの .dllを利用すると分かった。
試しに -static をつけて静的リンクを試みたところ、-lIL -ILU が見つからないとエラーが出てしまった。ググった感じでは、静的リンクをするなら 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 で消去(解放)しておかないといけない。
さておき。前述の問題の解決策が分かった。#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 というファイルをワークスペースフォルダ内に作って、以下の一文を書いておく。
vscode上で、Ctrl + Shift + P → C/C++:構成の編集(UI) C/C++: Edit Configurations (UI)。設定画面が出てくるので下のほうまでスクロールして「詳細設定」をクリック。「強制インクルード」の欄に以下を記述。
これで、vscode に vscode-preinclude.h が読み込まれて、そこに _UNICODE の定義を削除する一文が書いてあるから、DevIL の各関数が、wchar_t* ではなく char* を要求するものとして vscode に認識されて、おかしな警告が表示されなくなる。
あるいは、以下で紹介されているように、C++ の標準機能でファイルを開いてメモリに格納してから、そのメモリ領域を ilLoadL() に渡すのもアリかもしれない。
_The Developer's Image Library (DevIL) Tutorials
色々ググって、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
[ ツッコむ ]
以上です。