Qt5 OpenCV4 ライブラリ設定

Qt5 でOpenCV4を使用するときにライブラリの読み込みができなく結構苦労したのでめも書き

 環境
macOS Mojave 10.14
Qt Creator 4.9
Desktop Qt5.12.3 clang 64bit
opencv-4.1.0

pmakeの .proファイルに下記のように記載する

INCLUDEPATH += /usr/local/include/opencv4
QMAKE_LFLAGS +=  -L/usr/local/lib
LIBS += -lopencv_calib3d -lopencv_core -lopencv_dnn -lopencv_features2d -lopencv_flann -lopencv_gapi -lopencv_highgui -lopencv_imgcodecs -lopencv_imgproc -lopencv_ml -lopencv_objdetect -lopencv_photo -lopencv_stitching -lopencv_video -lopencv_videoio

参考にサイトでは下記の感じでできるようです。。(参考はUbuntuでやっていたのでmacとは違う?)

LIBS += `pkg-config opencv4 --cflags --libs`

私の環境ではLIBS += -L/usr/local/lib が受け付けてくれませんでした。QMAKE_LFLAGS += -L/usr/local/libというように書くと、ライブラリフォルダを認識するようです。

C++ string型をprintfで出力

C++11以降にあるstring型。すごく便利なのでよく使うのですが、printf出力では使えないと思ってました。ちょっと手を加えて、 string型を c_str() でcの文字列型に変換すればできる。これがわかったときには思わず感動しました。

#include <string>
#include <cstdio>

int main()
{
    std::string str = "Hello world!";
    std::printf("%s\n",str.c_str());

    return 0;
}

OpenCVのサンプルをやるだけで苦労した

将来的にOpenCVを使ってソフトを作るつもりなので、インストールしてサンプルファイルを駆動させようと思ったら、意外と苦労しました。C++でサンプルを動作させるだけなら、何年か前にもやったことがあるのですが、改めてやってみたらできませんでした。

環境

マシン macOS Mojave 10.14.

言語 C++

ライブラリ OpenCV 4.1.0

コンパイラ Clang / LLVM

実行するとどうしても、リンク異常が出てしてしまいます。参考は下記のURLです。

OpenCV/C++で画像処理入門 vol.1 〜画像を表示してみよう〜

ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

ソースからコンパイルして、cmakeを使って実行すると問題なくできました。

C++のOpenCVをCMakeで使う

いろいろ調べた結果、リンカエラーが発生するときは、ライブラリがきちんと割り当てられていなかったということ。pkg-configの設定ファイルが悪かったみたいです。余計なlibファイルも記載されていました。/usr/local/libフォルダからopencvに関連するのファイルを抽出し書き直しましたファイルはこちら。

opencv4.pc

prefix=/usr/local
exec_prefix=${prefix}
includedir=${prefix}/include
libdir=${exec_prefix}/lib

Name: opencv4
Description: The opencv library
Version: 4.0.0
Cflags: -I${includedir}/opencv4
Libs: -L${libdir} -lopencv_calib3d -lopencv_core -lopencv_dnn -lopencv_features2d -lopencv_flann -lopencv_gapi -lopencv_highgui -lopencv_imgcodecs -lopencv_imgproc -lopencv_ml -lopencv_objdetect -lopencv_photo -lopencv_stitching -lopencv_video -lopencv_videoio

pkg-configを実行すると

$ pkg-config opencv4 --cflags --libs
-I/usr/local/include/opencv4 -L/usr/local/lib -lopencv_calib3d -lopencv_core -lopencv_dnn -lopencv_features2d -lopencv_flann -lopencv_gapi -lopencv_highgui -lopencv_imgcodecs -lopencv_imgproc -lopencv_ml -lopencv_objdetect -lopencv_photo -lopencv_stitching -lopencv_video -lopencv_videoio

コンパイルと実行は下記のようにやる。ちなみにstd=c++11を指定しないとコンパイルができません。

$ clang++ -std=c++14 $(pkg-config --cflags --libs opencv4) main.cpp
$ ./a.out

AOJ 双方向連結リストで苦しむ

AizuOnlineJudge(AOJ)でアルゴリズムに入りましたが、だんだんと難しくなり、進むペースが遅くなってきました。それでもWeb上のヒントだけで何とか説いてきましたが、ALDS1_3_C でつまずきました。

StackやQueueと同じように配列でプログラムを組んで実行すると、10個目でタイムアウトで引っかかってしまいます。配列に挿入するときや削除するときには1個ずつずらすことをやっていたので、時間がかかりすぎたようです。そのプログラムがこれ。

https://github.com/lingmujianshi/AizuOnlineJudge/blob/master/ALDS1/ALDS1_3_C/ALDS1_3_C_timeout.cpp

他の人が解いた回答を見ましたが全く理解できませんでした。双方向連結リスト?番兵?わからない言葉ばかりです。

そこでとうとうこの本を買ってしまいました。

プログラミングコンテスト攻略のためのアルゴリズムとデータ構造 単行本(ソフトカバー)

本を見るとズバリと回答が書いてありました。双方向連結リストは自分の前の要素と次の要素へそれぞれのポインタを持つ構造体で番兵はリストの先頭を指す特別なノードで設置することで簡略化できるとのこと。

サンプルを丸写しし、投稿したら問題なく通りましたが、実を言うとコードを全然理解できませんでした。Visual Studio Codeのデバッグ機能を使ってみてみると、Node構造体の中身がぜんぜん表示されませんでした。Classを作ると確認する事ができました。サンプルはmallocを使っていたので、せっかくなのでnewを使って書き直しました。そのコードがこちら。

2つ目を追加したときの流れを書いてみたらやっと理解することができました。

    void insert(int key)
    {
        Node *x = new Node();
        x->key = key;
        // 番兵の直後に要素を追加
        x->next = nil->next;
        nil->next->prev = x;
        nil->next = x;
        x->prev = nil;
    }
insert() 図解

https://github.com/lingmujianshi/AizuOnlineJudge/blob/master/ALDS1/ALDS1_3_C/main.cpp#L32-L41

C++をWindows コマンドプロンプトでコンパイル

普段自宅ではMACBOOKで作業をしていますが、出張ででかけているときはWindowsを使っています。AOJでC++の問題を解くのに、Visual StudioのIDEをひらいてやってたこともあるのですが、プロジェクトを作って大掛かりになってしまうので、簡単にコマンドプロンプト(cmd)でやる方法をまとめます。

まず、コマンドプロンプトでコンパイルする場合はcl.exeを使います。これを直接実行すると、インクルードパスが設定されていません。と表示され実行っできません。

>cl main.cpp

Microsoft(R) C/C++ Optimizing Compiler Version 19.16.27030.1 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.
main.cpp
main.cpp(2): fatal error C1034: iostream: インクルード パスが設定されていません。

通常はスタートメニューから、「開発者コマンド プロンプト for VS 2017」を起動し実行します。 しかし、ディレクトリーに移動するのが非常に面倒です。

「開発者コマンド プロンプト for VS 2017」をプロパティで確認すると、

%comspec% /k "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat"

コマンドプロンプトでフォルダを指定して開く簡単な方法は、エクスプローラのアドレスバーにcmdと入力することです。そこで、上記で確認したコマンドを入力すると、コマンドプロンプトがコンパイル実行可能状態になります。

C:\Users\username\Documents\C\projects\helloworld>%comspec% /k "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat"
**********************************************************************
** Visual Studio 2017 Developer Command Prompt v15.9.11
** Copyright (c) 2017 Microsoft Corporation
**********************************************************************

cl.exeでは/EHscオプションをつけて実行します。

C:\Users\username\Documents\C\projects\helloworld>cl /EHsc main.cpp
Microsoft(R) C/C++ Optimizing Compiler Version 19.16.27030.1 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.
main.cpp
Microsoft (R) Incremental Linker Version 14.16.27030.1
Copyright (C) Microsoft Corporation.  All rights reserved.
/out:main.exe
main.obj

プログラム実行は、exeファイルを入力するだけ。今回はmain.exeができあがったので下記のようになります。

C:\Users\username\Documents\C\projects\helloworld>main.exe
Hello C++ World

Visual studio codeでターミナルでやるだけなら上記のやりかたで問題ありません。もしTaskで実行する場合は、実行毎に新規ターミナルが起動するのでできません。その場合は、Visual studio codeを一度閉じて、コマンドプロンプトで%comspec% /k "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\VsDevCmd.bat"を実行してからcode .と入力すると、Taskも実行できるようになります。フォルダも選択した状態で起動するので非常に便利です。

Qtではじめてのアプリをつくった。

最近はAizu Online Judgeでアルゴリズムの問題はやってましたが、 Qtの使い方の勉強で成果物が全然作ることができず、ブログの更新も全然できませんでした。C++やQtの初歩的なレベルのことは一通りやったので、このへんでなにか作ってみようと思い、Qtでアプリを作りました。

アプリといったも全く大したこともなく役に立つものではありませんが、自分の理解力を高めるためにアップします。

QLineEditで入力した数値をビットフィールドで作ったBits構造体にポインタで渡して、各ボタンのランプが点灯するようにしました。また、各ボタンを押したときには、Bits構造体を反転させるようにしています。

Bits構造体は下記の通り。

struct Bits
{
    int bit0:1;
    int bit1:1;
    int bit2:1;
    int bit3:1;
    int bit4:1;
    int bit5:1;
    int bit6:1;
    int bit7:1;
    int bit8:1;
    int bit9:1;
    int bit10:1;
    int bit11:1;
    int bit12:1;
    int bit13:1;
    int bit14:1;
    int bit15:1;
};

ポインタをキャストしてデータを参照しています。

Bits* bits; // Bits オブジェクト
int data; // 入力データ

data = ui->lineEdit_code->text().toInt();
bits = reinterpret_cast<struct Bits *>(&amp;data);

ボタンを押したときに、 bitを反転させます。ポインタで参照すしているのでbitsを変更すればdataの値も変わります。ボタンをおしたら、QLineEditとボタンに表示します。

void MainWindow::on_pushButton_b0_clicked()
{
    bits->bit0 = ~bits->bit0;
    setLineData();
    displayButtons();
}

https://github.com/lingmujianshi/QBitDisplay