【C++】MinGW (g++)とCMakeでOpenCVとopencv-contributeをビルドする(Windows)
バージョン
$ cmake --version cmake version 3.17.2 $ make -v GNU Make 3.81 $ mingw32-make -v GNU Make 4.2.1 $ g++ -v gcc version 8.1.0
はじめに
タイトルの通りです。開発環境がVisual Studioを使っているならば、ココの「Windows」ボタンからビルド済みのものをインストールできますが、g++, gccでビルドされたものは見つかりませんでした。
[追記]...と思っていたら、ありました!!
https://github.com/huihut/OpenCV-MinGW-Build
まぁでも最新バージョンではないっぽいし、自分でビルドすることにしました。
ということで、OpenCVの適当なバージョンをダウンロードして、cmakeで普通にやろうとしたのですが、
$ cmake [directry]
ってしても、抑もConfigureが通りません。そして、そのままmakeをしても、
gcc: error: long: No such file or directory C:\PROGRA~1\MINGW-~1\X86_64~1.0-P\mingw64\bin\windres.exe: preprocessing failed. make[2]: *** [modules/core/CMakeFiles/opencv_core.dir/vs_version.rc.obj] エラー 1 make[1]: *** [modules/core/CMakeFiles/opencv_core.dir/all] エラー 2 make: *** [all] エラー 2
もちろんですがエラーが出てしまいました。
ということで、CMakeのGUIを使って設定する必要があります。
ビルド方法
1. opencv-3.4 とcontributeをダウンロードする
ここ↓から、最新バージョンのOpenCVをgit cloneしてきます。
github.com
そして、ここ↓から最新バージョンのOpencv-contributeをgit cloneしてきます。
github.com
Contributeとは:OpenCvの追加物みたいなもので、顔認識やセグメンテーションのような機能を追加できます(機能一覧についてはここを見てください)。十分にテストされていないAPIなので、OpenCV本体と一緒にするべきではない、という考えのようです。
2. opencv-3.4の中にbuildフォルダを作る(名前は何でもOK)
みたいな感じです。
3. Cmake GUIを開く
CMakeGUIを開いて、一番上にある「Where is the source code」と「Where to build the binaries」を設定します。
ソースコードの位置は hoge/opencv-3.4、ビルド先にはhoge/opencv-3.4/build_mingw みたいな感じで先ほど作ったフォルダを指定します。
そして、Configureボタンを押します。
4. コンパイラをMinGWに指定する
Configureボタンを押すと、以下の様なコンパイラーを選択する画面が表示されます。
この写真のように設定してFinishを押します
7. 以下の様な設定にします。
Configureが終わると、未解決の項目が赤くなって大量に表示されているはずです。ここからビルド設定を行います。ここは個人の環境によって変わるかもしれませんが、僕の環境では以下の設定でビルドが通りました。
- BUILD_JAVAのチェックを外す
- BUIKD_PYTHONのチェックを外す
- BUILD_OPENEXRのチェックを外す
- WITH_IPPのチェックを外す
- ENABLE_PRECOMPILED_HEADERS のチェックを外す
- WITH_OPENGL のチェックを付ける
- OPENCV_ENABLE_NONFREEのチェックを付ける
- OPENCV_EXTRA_MODULES_PATH のパスを「hoge/opencv_contrib-3.4/modules」に設定する
- BUILD_opencv_worldのチェックを付ける(これをチェックすると、libファイルやdllファイルが「opencv_world_[version].dll」の様な名前で一つにまとめてくれる)
7. DOPENCV_ALLOCATOR_STATS_COUNTER_TYPEを追加
CMake GUIの右上に「Add Entry」というボタンがあるので、そこを押します。
そして、
- Name = DOPENCV_ALLOCATOR_STATS_COUNTER_TYPE
- Type = STRING
- Value = int64_t(上写真ではDescriptionの所に書き込んでいますがミスですm(__)m。Valueが正解)
となるように設定します。
このように表示されればOKです。
10. 「Configure」→「Generate」
これで、一通りの設定が終わったので、もう一度Configure をしてみましょう!
エラーが出ずに最後までConfigureできたら成功です。Generateもします。
11. コマンドプロンプトで「make」!
ここまで順調に来ていれば、mingw_buildフォルダ(2.で作成したフォルダ)にMakeFileがあるはずなので、
$ cd mingw_make #(2.で作成したフォルダ) $ make
でmakeします。
上手くいけば、以下のファイルが生成されているはずです
上の二つがあればビルドはできています!!(*'ω'*)
HelloWorld
適当にプログラムを作ります。HelloWorldという文字を出力するプログラムです。
//main.cpp #include <opencv2/opencv.hpp> int main() { cv::Mat image(200, 800, CV_8UC3); cv::String text = "Hello, world"; cv::putText(image, text, cv::Point(100,100), cv::FONT_HERSHEY_SIMPLEX, 3, cv::Scalar(255,255,0), 5, cv::LINE_AA);// テキストを描画 // 画像を表示 cv::imshow("hello_world", image); cv::waitKey(0); return 0; }
CmakeListsは以下のようになります。
# CmakeLists.txt cmake_minimum_required(VERSION 2.8) set(PROJECT_NAME Test) project(${PROJECT_NAME} CXX) set(OpenCV_DIR "hogw/mingw_build") #2.で作成したフォルダ find_package(OpenCV REQUIRED) if(OpenCV_FOUND) include_directories(${OpenCV_INCLUDE_DIRS}) link_libraries(${OpenCV_LIBS}) endif() add_executable(${PROJECT_NAME} main.cpp main.hpp) target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBRARIES})
$ cd (上で書いた.cppとCMakeFilesがあるディレクトリ) $ mkdir build $ cmake .. -G"MinGW Makefiles" # コンパイラをMinGWに指定する $ make
これでTest.exeが生成されればOKです!!
動的ライブラリファイル「(2.で作ったフォルダ)/bin/libopencv_world3412.dll」をTest.extと同じ階層にコピーしてきます。
これで、OpenCVのプログラムを実行できるはずです、、、!
HelloWorld実行
実行できたぁ~~
【C++/MinGW】FreeTypeを使ってTTFファイルから文字レンダリング
FreeTypeとは?
FreeTypeはフォントエンジンを実装したライブラリです。つまり、std::stringとかcharとかの文字列を画像に変換することができます。この時、フォントファイル(ttf等)を指定したり、太字/斜体でレンダリングすることできます。TrueType、Type1フォント、OpenTypeなどのフォント形式をサポートしているようです。
FreeTypeのインストール
まず、下のURKから、最新版のFreeTyoeをダウンロードしてきます。
FreeType Downloads
僕がダウンロードしたのはバージョン2.10.4です。
解凍して中身を見ると、CMakeListsがあったので、CMakeで入れることにします。
$ cd [ダウンロードしたFreeTypeライブラリのフォルダ] $ mkdir build $ cd build $ cmake .. -G "MinGW Makefiles" $ make
下から二行目の"MinGW Makefiles" はコンパイラを指定しているだけで、通常は付ける必要はありません。筆者の環境ではVisualStudioのコンパイラとg++が混在しているので一応指定しておいただけです。
makeコマンドがうまくいくと、作成したbuildフォルダの中に「libfreetype.a」ができるので、これをリンクすればOKです。
サンプルコード
#include <opencv2/opencv.hpp> #include <stdio.h> #include <math.h> #include <codecvt> #include <string> #include <iostream> #include <locale> #include <ft2build.h> #include FT_FREETYPE_H #define WIDTH 500 #define HEIGHT 300 class TextRender{ private: FT_Library library; /* handle to library */ FT_Face face; /* handle to face object */ unsigned char *image; void draw_bitmap(int x, int y); FT_GlyphSlot slot; // グリフへのショートカット std::u32string u32str; public: TextRender(); ~TextRender(); bool init(); bool setFont(const char *font_file_name, int char_width, int char_height); void drawString(const char text[]); unsigned char *getImage(); }; TextRender::TextRender():image(nullptr){ init();} // 1. libraryを初期化 bool TextRender::init(){ if(image != nullptr) free(image); image = nullptr; auto error = FT_Init_FreeType( &library ) ; if (error) { std::cerr << "[ERROR] Failed to init FreeType library!" << std::endl; } return !error; } //フォントファイルを読み込む bool TextRender::setFont(const char *font_file_name, int char_width, int char_height){ // 2. faceを作成 auto error = FT_New_Face( library, font_file_name, 0, &face); if ( error == FT_Err_Unknown_File_Format ){ std::cerr << "[ERROR] Font file format is not supported!! " << std::endl; return false; }else if ( error ){ std::cerr << "[ERROR] Font file not found or it is broken! " << std::endl; return false; } // 3. 文字サイズを設定 error = FT_Set_Char_Size(face, 0, char_width*char_height, // 幅と高さ 300, 300); // 水平、垂直解像度*/ slot = face->glyph; // グリフへのショートカット return !error; } void TextRender::drawString(const char text[]){ image = new unsigned char[WIDTH*HEIGHT]; std::u32string u32str = std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t>().from_bytes(text); int curPosX = 0; int curPosY = 60; //現在のカーソル位置 int last_height = 0; //最後に文字を書いたときの文字の大きさ for (int n = 0; n < u32str.size(); n++ ){ if(u32str[n] == '\n'){ curPosX = 0; curPosY += last_height + 20; }else{ if (FT_Load_Char( face, u32str[n], FT_LOAD_RENDER )) continue; //一文字レンダリング // int yMax = face->bbox.yMax; // int yMin = face->bbox.yMin; // int baseline = bitmap->rows * yMax / (yMax - yMin); draw_bitmap(curPosX, curPosY - slot->bitmap_top); //imageにslot->bitmapの中身をコピーする } last_height = (slot->bitmap).rows; curPosX += slot->advance.x >> 6; curPosY += slot->advance.y >> 6; } }; //生成された位置も自分の画像データをimageにコピーする void TextRender::draw_bitmap(int x, int y){ int i, j, p, q; const int x_max = x + (slot->bitmap).width; const int y_max = y + (slot->bitmap).rows; for ( i = x, p = 0; i < x_max; i++, p++ ){ for ( j = y, q = 0; j < y_max; j++, q++ ){ if ( i < 0 || j < 0 || i >= WIDTH || j >= HEIGHT) continue; image[j*WIDTH + i] |= (slot->bitmap).buffer[q *(slot->bitmap).width + p]; } } } unsigned char *TextRender::getImage(){ return image; } TextRender::~TextRender(){ if(image != nullptr) free(image); FT_Done_Face ( face ); FT_Done_FreeType( library ); } int main(){ TextRender TR; TR.setFont("Meiryo.ttf", 16, 34); //レンダリング unsigned char *image; const char text[] = "こんにちは、世界!\nHello, World!!"; TR.drawString(text); image = TR.getImage(); //画像を表示 cv::Mat image_cv(HEIGHT, WIDTH, CV_8UC1,image); cv::imshow("output", image_cv); cv::waitKey(0); return 0; }
上のコードをコンパイルする為のCmakeListtsも載せておきます。
CmakeListsでしていることは
- OpenCVを見つけていれる
- [ダウンロードしたFreeTypeライブラリ]/includeフォルダを#includeでの読み込み対象にする
- libfreetype.aをリンクする
- main.cpp(上のプログラムのこと)から実行ファイルを生成
という感じです
cmake_minimum_required( VERSION 3.5 ) project(test CXX) set(OpenCV_DIR "C:/Users/Owner/Library/Opencv/opencv-3.4/build_mingw") find_package(OpenCV REQUIRED) if(OpenCV_FOUND) include_directories(${OpenCV_INCLUDE_DIRS}) link_libraries(${OpenCV_LIBS}) endif() set(FREETYPE_DIR "C:/Users/Owner/Library/freetype-2.10.4") include_directories(${FREETYPE_DIR}/include) add_executable( test main.cpp) target_link_libraries( test ${FREETYPE_DIR}/build/libfreetype.a ${OpenCV_LIBRARIES})
実行結果
下の画像の様な結果になります
Meiryo.ttfが読み込まれて日本語もきちんと表示できているのがわかると思います
FreeTypeの使い方
初期化
まず、こんな感じでincludeします
#include <ft2build.h> #include FT_FREETYPE_H
次に、色々初期化します。
FT_Library library; FT_Face face; // 1. libraryを初期化 auto error = FT_Init_FreeType( &library ) ; if (error) {... } // 2. faceを作成 error = FT_New_Face( library, "hoge.ttf", 0, &face); if (error) {... } // 3. 文字サイズを設定 error = FT_Set_Char_Size(face, 0, 16*54, // 幅と高さ 300, 300); // 水平、垂直解像度*/
ここでいじるのはフォントファイル名と文字サイズくらいだと思います
太字or斜体に設定する
太字や車体などの設定は、以下の関数を使ってグリフに適応させます
FT_GlyphSlot_Oblique(face->glyph ); //斜体にする FT_GlyphSlot_Embolden(face->glyph );//太字にする
これらを使うにはグリフを読み込む必要があり、TextRender::drawString関数のfor文の中身を以下の様に変更する必要があります。
for (int n = 0; n < u32str.size(); n++ ){ if(u32str[n] == '\n'){ curPosX = 0; curPosY += last_height + 20; }else{ // 追加!今からレンダリングする式のグリフを取得 FT_UInt glyph_index = FT_Get_Char_Index( face, u32str[n] ); // 追加!そのグリフの設定を読み取る(ここで1bitビットマップに設定したりカラー画像にしたり色々変えられる) auto error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); if ( error )continue; /* ignore errors */ // 追加! FT_GlyphSlot_Oblique(face->glyph ); //斜体にする FT_GlyphSlot_Embolden(face->glyph );//太字にする //追加!1文字レンダリング! error = FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL ); if ( error ) continue; draw_bitmap(curPosX, curPosY - slot->bitmap_top); //imageにslot->bitmapの中身をコピーする } last_height = (slot->bitmap).rows; curPosX += slot->advance.x >> 6; curPosY += slot->advance.y >> 6; }
上のプログラムの様に、各文字のグリフを取得して、それ経由でレンダリングすることもできます。その時に、太字は斜体などの設定をグリフに適応させる、という事でしょう。
ちなみに、最初のプログラムで使っていた FT_Load_Char関数とごちゃ混ぜにするとフリーズするので気を付けてください。
また、FT_GlyphSlot_Oblique関数とFT_GlyphSlot_Embolden関数は freetype/ftsynth.h で宣言されているので、冒頭のinclude文を
#include <ft2build.h> #include <freetype/ftsynth.h> #include FT_FREETYPE_H
に書き直してください
参考サイト→FreeType2での太字、斜体 - HYPERSPACE UNIVERSE NEWS
実行するとこうなります:
レンダリング(方法1)
FT_Load_Char( face, ’a’, FT_LOAD_RENDER)
このように書くことで、'a'という一文字がレンダリングされて、face->glyph.bitmapにビットマップとして格納されます。(上のプログラムでは、slot = face->glyph;というショートカットを作成しているので、slot->bitmapと書いています)
このビットマップのサイズ等は以下の様に取得できます
値 | 計算 |
横幅 | slot->bitmap.width |
高さ | slot->bitmap.rows |
yMax | face->bbox.yMax; |
yMin | face->bbox.yMin |
文字のベースライン | bitmap->rows * yMax / (yMax - yMin) |
yMinとyMaxが何を意味するのか分かりませんが、文字のベースラインを計算するにはこの値を使うみたいです。
レンダリング(方法2)
「太字or斜体にする」の節で使用した、グリフに基づくレンダリング方法です
// 文字'a'のグリフを取得 FT_UInt glyph_index = FT_Get_Char_Index( face, 'a'); auto error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );// そのグリフの設定を読み取る FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL );//1文字レンダリング!
行数が上の方法1の三倍になっていますが、やっていることは同じで「a」をface->glyph.bitmapにビットマップとして格納します
【Python】無料でジオコーディング(住所→緯度経度etc.)
住所⇔緯度&経度の変換をしたい、目的地までのルート検索や所要時間を調べたい、と思い良いものがないかなぁと思っていました。
すると、↓の記事でGoogle Maps APIというものがあることを知り、
qiita.com
使ってみよ~~、と思ったのですが、なんと有料でした!
一学生として有料は厳しい、、、、。で調べていくと、OSM(Open Street Map)が提供しているデータを使えばジオコーディングが無料でできるらしい。おお~~(≧▽≦)
geopyというものを使うと、住所→緯度&軽度の取得ができます
- Python 3.7.3 on win32
- geographiclib-1.50 geopy-2.0.0
$ pip install geopy
でgeopyを入れます。
プログラム
# -*- coding: utf-8 -*- from geopy.geocoders import Nominatim def main(): geolocator = Nominatim(user_agent="test-dayo") location = geolocator.geocode("日本 北九州") print("Lat, long = ",location.latitude, location.longitude) print("full address = ", location.address) # 辞書として読み取る loc_dict = dict(location.raw) print("Lat, long = ", loc_dict["lat"], loc_dict["lon"]) print("full address = ", loc_dict["display_name"]) print("class and type = ", loc_dict["class"], loc_dict["type"]) url = f"https://www.google.com/maps/search/?api=1&query={location.latitude, location.longitude}" import webbrowser webbrowser.open(url) main()
上のプログラムを実行すると、
Lat, long = 35.675886399999996 139.74505141191034 full address = 国会議事堂, 1, 国道246号, 永田町1, 永田町, 千代田区, 100-0014, 日本 (Japan) Lat, long = 35.675886399999996 139.74505141191034 full address = 国会議事堂, 1, 国道246号, 永田町1, 永田町, 千代田区, 100-0014, 日本 (Japan) class and type = office government
と表示され、ブラウザで国会議事堂のGoogleMapが開くと思います。
ほかのライブラリ
他にも数種類GeoCodingをするPythonライブラリがあるみたいです。
GeoCoder
$ pip install geocoder
でgeocoderを入れます。
import geocoder # 下のコードはGoogleAPIの登録が必須になってから動かなくなっている # g = geocoder.google('Mountain View, CA') # print(g.latlng) location = '日本 国会議事堂' ret = geocoder.osm(location, timeout=5.0) print(ret.latlng) # 出力結果:[35.675886399999996, 139.74505141191034]