画像処理ライブラリ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版へ移行するのが吉です.
バイナリパッケージがなければ,ソースからビルドすればいいじゃない.(とても面倒でした.)