0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

cv::highguiにまつわるエトセトラ

Posted at

画像処理ライブラリOpenCVのGUI機能の(意外にハマりがちな?)環境依存性に関して,少しだけ解決を試みます.
想定環境として,OSはLinux,言語はC++ですが,WinとかPythonにも通底と思われます.

OpenCVのGUIバックエンド

OpenCVのGUI機能は,他のライブラリに依存していて,環境によって挙動が異なります.
Linuxの場合,Fedora等ではQt版,Ubuntu等ではGTK版が標準的なようです.

まずは,利用環境のGUIバックエンドを確認してみましょう:(Fedoraの場合)

$ cvinfo
    # 環境によっては,このコマンドが存在しないようです.
...
    GUI:    QT5
...

$ ldd /usr/lib64/libopencv_highgui.so
    # 環境によって,ライブラリのパスが異なるでしょう.
...
    libQt5...
...

(Ubuntuの場合)

$ ldd /usr/lib/x86_64-linux-gnu/libopencv_highgui.so
...
    libgtk-3.so...
...

基本ソースコード

具体例として,ウェブカメラのモニタ表示アプリを作成してみましょう:

$ vi cam.c
#include <opencv2/opencv.hpp>
#include <string>
int main(void) {
    std::string         win = "camera";
    cv::Mat             img;
    cv::VideoCapture    cam(0);
/*
    int         w = 1280, h = 720;  // 撮影画像のサイズ.適宜調整
    cam.set(cv::CAP_PROP_FRAME_WIDTH, w);
    cam.set(cv::CAP_PROP_FRAME_HEIGHT, h);
*/
/*
    cv::namedWindow(win, cv::WINDOW_NORMAL);    // 可変サイズの窓
    cv::resizeWindow(win, w, h);
*/
    while (1) {
        if (!cam.read(img)) continue;
        cv::imshow(win, img);
        int key = cv::waitKey(30);
        if (key == 'q') break;      // キー[Q]で終了
    }
    return 0;
}
$ g++ cam.c `pkgconf --cflags --libs opencv4` -o cam
$ ./cam

エラー処理や後処理の省略された超手抜コードですが,大抵の環境では動く筈です.

とりあえず,カメラの撮影像がウィンドウに表示されますね.
もし,窓サイズやフレームレートに問題がある場合,cam.set()のパラメータ調整で解決できるでしょう.

また,キー[Q]の入力で終了しますね.

しかし,やらかしがちですが,ウィンドウのクローズボタンでは終了できません.
まあ,終了機能を実装していないので当然ですね.
何もしてなくて...❎.

そして,大抵の画像処理アプリでは,全画面化機能も欲しいですね.

ウィンドウクローズ検出機能

まずは,❎をとりもどせ!!
閉窓検出関数isClosed()を実装しましょう:

...
int isClosed(std::string win) {
    // GTK以外に共通の方法...
    switch ((int)cv::getWindowProperty(win, cv::WND_PROP_VISIBLE)) {
    case 0: return 1;   // ❌閉まったよー(GTK以外)
    case 1: return 0;   // ⭕開いてるよー(GTK以外)
    case -1: break;     // ❓わかりません(GTK)
    }
    // GTKだけ別の方法...
    try {
        cv::getWindowProperty(win, cv::WND_PROP_FULLSCREEN);
    } catch (...) {     // カッコ内「...」は,そのまま「...」です
        return 1;       // ❌閉まったよー(GTK)
    }
    return 0;           // ⭕開いてるよー(GTK)
}
int main(...) {
    ...
        int key = cv::waitKey(...);
        if (isClosed(win)) break;   // ✅追加.ボタン❎でも終了
    ...
}

可視性フラグWND_PROP_VISIBLEによる検出方法は,ネット上の各所で紹介されています.
ただし,Qt版では有効ですが,GTK版では無効でした.

そこで,試行錯誤の末,この全画面フラグWND_PROP_FULLSCREENに辿り着きました.

GTK版のgetWindowProperty()は,可視性フラグに対して,常に-1を返します.
また,全画面フラグに対して,閉窓状態の場合に例外を送出します.

しかし,謎ですね...なぜ全画面フラグなのか?閉窓なのに?
OpenCV側の実装的に「へー,そーですか」としか...

これで,どのGUI環境でも,クローズボタン❎によるアプリ終了が可能になったかと思われます.

フルスクリーン化機能

全画面/ウィンドウ表示の切替関数togFull()を実装します.

...
void togFull(std::string win) {
    cv::setWindowProperty(win, cv::WND_PROP_FULLSCREEN,
        !(int)cv::getWindowProperty(win, cv::WND_PROP_FULLSCREEN));
}
int main(...) {
    ...
    cv::namedWindow(win, cv::WINDOW_NORMAL);    // ✅有効化.可変サイズの窓の生成
    ...
    while (1) {
        ...
        if (key == 'f') togFull(win);   // ✅追加.キー[F]で表示切替
    }
    ...
}

ウィンドウは,基本ソースのようにnamedWindow()を省略していても,画像表示imshow()に伴い,自動生成されていました.
しかし,その場合,モードがデフォルトでWINDOW_AUTOSIZE(固定サイズ)となるので,全画面化の際に不都合が生じてしまいます.

可変サイズモードでは,窓枠ドラッグとかによって,窓サイズとともに画像サイズも変更されます.
全画面状態では,画像が拡大されます.

一方,固定サイズモードでは,窓を全画面化しても画像は拡大されません.

そして,窓生成後のモード変更についても環境依存です.
Qt版では変更可能ですが,GTK版では不可能なのです.

したがって,モードをWINDOW_NORMAL(可変サイズ)としておくのが無難です.

ウィンドウモード変更機能

環境依存な実験です.
窓サイズの固定/可変モードの切替関数togAuto()を実装してみましょう:

...
void togAuto(std::string win) {
    cv::setWindowProperty(win, cv::WND_PROP_AUTOSIZE, 
        !(int)cv::getWindowProperty(win, cv::WND_PROP_AUTOSIZE));
}
int main(...) {
    ...
        if (key == 'a') togAuto(win);   // ✅追加.キー[A]でモード切替
    ...
}

Qtでは,固定モードでの全画面化とかもできます.
しかし,挙動不審でもありました.

GTKでは,モード切替自体ができないようです.

動作検証環境

  • Qt版:OpenCV 4.8,Qt 5, Fedora 39
  • GTK版:OpenCV 4.5,GTK 3, Ubuntu 22.04

検証不足です.LinuxのQt版とGTK版だけでしか確認していません.
どなたか,WinとかMacで検証plz.

まとめ

OpenCVのGUI環境依存性について一部を統一しました.
しかし,完全統一は無理そうです.

GTK版OpenCVのユーザはQt版へ移行するのが吉です.
バイナリパッケージがなければ,ソースからビルドすればいいじゃない.(とても面倒でした.)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?