fc2ブログ

OpenCVとPyQtを組み合わせて使う

 OpenCVはこのブログの中で何度も取り上げていますが、C++でプログラムを書いて、標準ウィンドウに画像処理結果を出す場合が多かったです。最近はPython環境を構築し、そちらでコードを実行する方がはるかに楽なので、C++を使う必要性もなくなってきています。ただ、C++もそうなんですが、OpenCVを実装したFormアプリを作るのは結構面倒なんですよね。

 以前のブログでPyQtを使ってGUIを作成したことがありました。DesignerソフトでGUIを設計し、作成されたuiファイルをpyファイルに変換して実装しました。ただ、このやり方だと、GUIのコントロール関係の情報がPythonコード内に組み込まれることで、コードが膨大になり、大変見にくくなって気になっていました。
 また、tkinterでGUI実装したこともありました。ただし、tkinterのGUIって安っぽくて美しくないんですよね。(文句ばっかりですね・・)

 今回、PyQtのuiファイルをpyファイルに変換し実装しなくても済む方法を見つけたので、試してみました。いまさらですが・・。
 まず、DesignerソフトでGUIを作成しました。ここでの注意点は、画像はラベル(QLabel)で表示させることです。少し違和感がありますが・・。Qt_gui1_201026.pngメニューバーのFileにOpenとQuitのサブメニューを作成し、Qt_gui2_201026.pngImage ProcessingにGray scale transformation(グレー変換)とBinarization(二値化)のサブメニューを作成しました。Qt_gui3_201026.png作成後、uiファイル(OpenCV_Developer.ui)に保存しました。ファイルの中身は以下の通りです。
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>680</width>
<height>540</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QLabel" name="viewlabel">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>640</width>
<height>480</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>23</height>
</rect>
</property>
<widget class="QMenu" name="menuFile_F">
<property name="title">
<string>File(&F)</string>
</property>
<addaction name="actionOpen_O"/>
<addaction name="actionQuit_Q"/>
</widget>
<widget class="QMenu" name="menuImage_Processing_P">
<property name="title">
<string>Image Processing(&P)</string>
</property>
<addaction name="actionGray_scale_transformation_G"/>
<addaction name="actionBinarization_B"/>
</widget>
<addaction name="menuFile_F"/>
<addaction name="menuImage_Processing_P"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionOpen_O">
<property name="text">
<string>Open(&O)</string>
</property>
</action>
<action name="actionQuit_Q">
<property name="text">
<string>Quit(&Q)</string>
</property>
</action>
<action name="actionGray_scale_transformation_G">
<property name="text">
<string>Gray scale transformation(&G)</string>
</property>
</action>
<action name="actionBinarization_B">
<property name="text">
<string>Binarization(&B)</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

 次にPythonコードですが、GUIのイベント関係のコード(OpenCV_Developer.py)と画像処理関係のコード(ImageProc.py)の2つに分けました。以下、作成したコードです。
# OpenCV_Developer.py
import sys
from PyQt5 import uic, QtWidgets
from PyQt5.QtGui import QImage, QPixmap
import ImageProc

class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui, self).__init__()
uic.loadUi('OpenCV_Developer.ui', self)
self.actionOpen_O.triggered.connect(self.func_Open)
self.actionQuit_Q.triggered.connect(self.func_Quit)
self.actionGray_scale_transformation_G.triggered.connect(self.func_Gray_scale_transformation)
self.actionBinarization_B.triggered.connect(self.func_Binarization)
self.show()

def func_ViewDisp(self, input_image):
if len(input_image.shape) == 2: # gray image
image = QImage(input_image.data, ImageProc.img_width, ImageProc.img_height, QImage.Format_Grayscale8)
elif len(input_image.shape) == 3: # color image
image = QImage(input_image.data, ImageProc.img_width, ImageProc.img_height, QImage.Format_RGB888)
pixmap = QPixmap.fromImage(image)
self.viewlabel.setPixmap(pixmap)

def func_Open(self):
input_image = ip.LoadImage('fossa.png')
self.func_ViewDisp(input_image)

def func_Quit(self):
ret = QtWidgets.QMessageBox.information(None, "Quit this software?", "Press a button!", QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
if ret == QtWidgets.QMessageBox.Yes:
sys.exit()

def func_Gray_scale_transformation(self):
gray_image = ip.GrayScaleTransformation(ImageProc.input_image)
self.func_ViewDisp(gray_image)

def func_Binarization(self):
bin_image = ip.Binarization(ImageProc.gray_image)
self.func_ViewDisp(bin_image)

app = QtWidgets.QApplication(sys.argv)
window = Ui()
ip = ImageProc.ImageProc()
app.exec_()
10行目でuiファイルを読み込んでいます。17〜23行目でGUI上のlabelオブジェクトに画像をモノクロ/カラーに分けて描画します。
 画像処理部のコードは以下の通りです。
# ImageProc.py
import cv2
import numpy as np

class ImageProc:

def LoadImage(self, filename):
global input_image
global img_height, img_width
input_image_temp = cv2.imread(filename, cv2.IMREAD_COLOR)
input_image = cv2.cvtColor(input_image_temp, cv2.COLOR_BGR2RGB)
img_height, img_width = input_image.shape[:2]
return input_image

def GrayScaleTransformation(self, inp_image):
global gray_image
gray_image = cv2.cvtColor(np.uint8(inp_image), cv2.COLOR_RGB2GRAY)
return gray_image

def Binarization(self, inp_image):
global bin_image
ret, bin_image = cv2.threshold(inp_image, 100, 255, cv2.THRESH_BINARY)
return bin_image

 以下、実行結果です。「File-Open」を実行すると画像がOpenCVのimreadコマンドで読み込まれ、最終的にGUI上に表示されました。Qt_OpenCV1_201026.png次に「Image Processing-Gray scale transformation」を実行しました。Qt_OpenCV2_201026.pngその画像を二値化するとQt_OpenCV3_201026.png目玉からビームの画像になりました。画像処理がうまく行ってGUI上に表示されていますね。
最後に「File-Quit」を実行すると終了の有無を確認してきますので、Yesを押して終了しました。Qt_OpenCV4_201026.png
 今後、暇を見つけて、このGUIに種々の機能追加をしていこうと思います。
スポンサーサイト



Ubuntuの.NET CoreでGUIを使う(2)

 前回の続きで、QMLファイルを使って.NET Coreに実装する内容をお話します。
 前回、Qt Creatorで作成したQMLファイルは以下の通りです。テキストボックスやボタン等のコントロールのプロパティとクリックイベント(37、49行目)が記録されています。
# Qt_test.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.15

Window {
id: window
visible: true
width: 320
height: 240
title: qsTr("Hello World")

Text {
id: element
x: 44
y: 117
width: 229
height: 81
color: "#ff0000"
layer.smooth: false
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
fontSizeMode: Text.FixedSize
clip: false
textFormat: Text.PlainText
font.pixelSize: 20
}

Button {
id: button1
x: 44
y: 44
text: qsTr("Show")

Connections {
target: button1
onClicked: element.text= "Hello world!"
}
}

Button {
id: button2
x: 178
y: 44
text: qsTr("Clear")

Connections {
target: button2
onClicked: element.text = ""
}
}
}
 まず、Ubuntuのターミナルから以下のコマンドでプロジェクト(プロジェクト名:QtTest)を作成しました。
dotnet new console -o QtTest
次に、作業フォルダに移動して、
cd QtTest
QML.NETライブラリをインストールしました。
dotnet add package Qml.Net.LinuxBinaries
この際、追加ライブラリのインストール要求がありましたので、以下のコマンドを実行。
sudo apt install libc6-dev
 次に、メインプログラムのProgram.csを以下の通り編集しました。18行目でQMLファイルを読み込んでいます。
// Program.cs
using System;
using Qml.Net;
using Qml.Net.Runtimes;

namespace QtTest
{
class Program
{
static int Main(string[] args)
{
RuntimeManager.DiscoverOrDownloadSuitableQtRuntime();

using (var app = new QGuiApplication(args))
{
using (var engine = new QQmlApplicationEngine())
{
engine.Load("Qt_test.qml");
return app.Exec();
}
}
}
}
}
コード作成後、以下のコマンドでビルドしました。
dotnet build
正常にビルドできれば、実行ファイル(dllファイル)は「bin/Debug/netcoreapp3.1」フォルダに作成されますので、移動して実行しました。
cd bin/Debug/netcoreapp3.1
dotnet ./QtTest.dll
この時、QMLファイル(Qt_test.qml)はdllファイルと同じフォルダ、つまり「bin/Debug/netcoreapp3.1」に入れておきます。実行すると、無事にフォームが立ち上がりました。dotNet_gui1_200823.pngShowボタンを押すと「Hello world!」が表示され、Clearボタンで表示が消去されることも確認できました。dotNet_gui2_200823.png Ubuntuの.NET Core(C#)でもQtを利用することで気軽にGUIが使えることが分かりました。また、別案件で使ってみようと思います。

Ubuntuの.NET CoreでGUIを使う(1)

 自宅では全くWindowsマシンを使わないのですが、Ubuntu、MacOSマシンでC#を使いたくて「.NET Core」を触ることがたまにあります。この時、GUIが利用できないので、Consoleアプリでプログラミングすることがほとんどです。.NET5以降でどうなるかは分かりませんが、現在(2020年8月時点)、最新の.NET Core3.1でGUI(Windows Form)をサポートしているのはWindowsOSのみという認識です。

 そのような状況で、.NET Core(Ubuntu, MacOS)でもGUIを簡便に使える方法がないかとネットで探していたら、「Qml.NET」なるものを見つけました。中身はGUIツールキットとして有名なQt(キュート)を使う方法です。昔PythonでPyQtを使ったことがありますが、今回はそれをC#(.NET)で使う試みです。早速、興味本位で試してみました。

 まず、準備として、Qt Creatorをインストールする必要がありました。Qt Creatorは昔々MacBook Airに入れておいたのですが、ディスク容量が半端ないぐらい多かったので、削除したことを思い出しました。今回、MacBook Airで再インストールを試みていたのですが、途中で「Xcodeのインストール云々」と言われてきたので、取りやめることにしました。Xcodeも以前MacBook Airに入れていたのですが、これも容量が多く、全く使わないので削除していました。結局、MacOSは取りやめて、Ubuntuマシンで行うことにしました。
 
 インストールはQtのダウンロードサイトから無償の「Downloads for open source users」から「Go open source」を選び、QtCreator_install1_200816.png「Get Started」の「Download the Qt Online Installer」を選びました。QtCreator_install2_200816.pngダウンロードされたファイルは「qt-unified-linux-x64-3.2.3-online.run」で、その後の処理は参考サイトの通り進めました。インストールの所要時間は1時間程度でひたすら待ちました・・。

 インストールも無事に終わり、アプリの中から「Qt Creator」を選択し、起動。QtCreator1_200816.png以後、GUIを作成するプロセスに入りますが、「Qml.NET」ではQML(Qt Modeling Language)ファイルが必要になりますので、その作成の流れを説明します。
1.「プロジェクト」-「+ New」を選択QtCreator2_200816.png2.新しいプロジェクトで「他のプロジェクト」-「Qt Quick UI Prototype」を選び、「選択ボタン」を押す。QtCreator3_200816.png3.プロジェクトパスの名前にプロジェクト名を入力し、次へQtCreator4_200816.png4.プロジェクトの詳細定義は、「デフォルト(Qt 5.12)」のままで次へQtCreator5_200816.png5.キットの選択も「デフォルトのまま」で次へQtCreator6_200816.png6.プロジェクトの管理で「完了ボタン」を押すとQtCreator7_200816.png「編集」画面が立ち上がりました。QMLファイルのコードを記入するウィンドウです。QtCreator8_200816.png「デザイン」に切り替えると、Visual Studioに似たフォームエディタやフォームの部品となるエレメントが表示されました。QtCreator9_200816.png確認すると、フォームの部品(Control)が表示されていなかったので、「QML Imports」から「インポートを追加」し、「QtQuick.Controls 2.15」を導入しました。QtCreator10_200816.png表示を「エレメント」に戻すとFormのControlが追加されたことが分かりました。QtCreator11_200816.pngFormにbuttonを2つ(button1、button2)、テキストボックス(element)を設置し、button1(Show)を押すと「Hello world!」をテキストボックスに表示し、button2(Clear)を押すと表示を消すようにしました。イベントハンドラは、右下の「ターゲット - シングルハンドラ - アクション」部分に記載することで、QMLファイルにも反映されました。左下の「実行」ボタンを押すことで、作成したフォーム(GUI)の動作確認も出来ました。QtCreator12_200816.png
次回は作成したQMLファイルを使って.NET Coreに実装する内容をお話します。

Ubuntu 20.04でOpenCV 4.3.0を使う(2)

 前回の続きです。今回はOpenCVの画像処理部を「共有ライブラリ」にして「.NET Core(C#)」から読み出す試みです。共有ライブラリ(Shared Library)は、WindowsでいうDLL(Dynamic Link Library)のことで、Linux(Unix系)ではso(Shared Object)ファイルのことです。

 今回作成する共有ライブラリ「image_proc_sl.so」のソースコードは以下の通り。前回の画像表示プログラムを参考に、カラーとグレー画像を表示する単純なものとしました。
// image_proc_sl.cpp
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>

extern "C" int ImageProc()
{
cv::Mat img;
img = cv::imread("dog.jpg", cv::IMREAD_COLOR);
cv::namedWindow("image", cv::WINDOW_AUTOSIZE);
cv::imshow("image", img);

cv::Mat gray_img;
gray_img = cv::imread("dog.jpg", cv::IMREAD_GRAYSCALE);
cv::namedWindow("Gray_image", cv::WINDOW_AUTOSIZE);
cv::imshow("Gray_image", gray_img);

cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
以下のコマンドでビルドして「*.so」ファイルを作成しました。
g++ -shared -fPIC image_proc_sl.cpp -o image_proc_sl.so -I/usr/local/include/opencv4/ -L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_highgui

 次に読み出す側の「.NET Core」のコードを作成します。ターミナルから以下のコマンドを打ち込むと「OpenCV_test」フォルダが作成され、
dotnet new console -o OpenCV_test
そのフォルダ内の「Program.cs」を以下の通り編集しました。
// Program.cs
using System;
using System.Runtime.InteropServices;
using System.IO;

namespace OpenCv_test
{
class Program
{
[DllImport("./image_proc_sl.so")]
static extern int ImageProc();

static void Main()
{
ImageProc();
}
}
}
このフォルダ(この場合はOpenCV_testフォルダ)で以下のコマンドを実行するとビルドされました。
dotnet build
ビルドされた実行ファイル(OpenCv_test.dll)は「bin/Debug/netcoreapp3.1」という深い階層の中にありますので、先程作成した「image_proc_sl.so」ファイルを「OpenCv_test.dll」と同じフォルダにコピーしました。合わせて、画像ファイルも同様に同じフォルダにコピーしました。その移動先のフォルダで以下のコマンドで実行させました。
dotnet ./OpenCV_test.dll
実行結果は以下の通りです。dotnetCoreRun_Result_200724.png問題なく、動作しました。

 余談ですが、この共有ライブラリを「.NET Core」から実行成功するまでに「コアダンプエラー」に引っかかってうまく行きませんでした。結局は試行錯誤している内にうまく行ったのですが、うまく行った原因がよく分かっていません。不具合の再現性が取れずじまいです。やったことは、試行錯誤の中で生成された数多くの失敗ファイル類を一層(削除)して、新規にコードを作り直しただけなんですけどね。何か悪さをしていたファイルがあったのでしょうか・・?
 また、新しい共有ライブラリを別に作って、問題なく「.NET Core(C#)」から読み出せて、動作することを確認しようと思います。

Ubuntu 20.04でOpenCV 4.3.0を使う(1)

 久々にOpenCVを触りたくなって、最新版4.3.0(20/04/06 Release)をUbuntu 20.04にインストールしました。最近、Python環境からはpipコマンドで簡単にOpenCVをインストール出来ますが、今回はPythonではなく、C++でコーディングするための環境です。

 Ubuntuの「.NET Core環境」でOpenCVを使いたいのですが、
① Python上のOpenCV環境との連携の仕方が良く分からない
② OpenCvSharpもチャレンジしたがうまく行かない(18.04まではサポート、20.04はまだ?)
の問題がありました。

 取り急ぎ、C++でOpenCVのコードを組んで「.NET Core」からコールする作戦に切り替えました。.NET CoreからわざわざOpenCVをコールせずに、「C++のみ」ですべてをコーディングすれば済むことですが、OpenCVの画像処理部だけを独立した「共有ライブラリ」のように使いたいのが今回の主旨です。

 以下、OpenCVのインストール手順の備忘録です。
 まず、OpenCVのWebサイトの「Releases」から「Sources」を選び、opencv-4.3.0.zipをダウンロードしました。Opencv_install_200712.pngzipファイルをhomeフォルダに解凍し、以下のコマンドを順番に実行しました。cmakeのパラメータ設定はデフォルトのままで、buildには時間がかかりますので、気長に終了まで待ちました。
cd opencv-4.3.0
mkdir build
cd build
cmake ..
cmake --build .
make -j4
sudo make install
sudo ldconfig
エラーなくすべて完遂しました!まずは一安心。昔はbuildで頻繁にエラーが出て悩まされましたね・・。エラーが出た場合は、cmakeのパラメータを自分のPC環境に合わせる必要があります。Python環境へのOpenCVのインストール(コマンド一発)に対し、相変わらず大変面倒な作業でした。
インストールされたヘッダファイルやライブラリは以下のフォルダにありました。
header files:   /usr/local/include/opencv4/opencv2
librarIes: /usr/local/lib

 インストールが完了したので、以下のコードを作成し、動作確認しました。画像ファイル(dog.jpg)はソースコードと同じフォルダに入れておきました。
// opencv_test.cpp
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>

int main(){
cv::Mat img;
img = cv::imread("dog.jpg", cv::IMREAD_COLOR);
cv::namedWindow("image", cv::WINDOW_AUTOSIZE);
cv::imshow("image", img);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
ビルドは以下のコマンドで行い、ライブラリとリンクさせました。
g++ opencv_test.cpp -o opencv_test -I/usr/local/include/opencv4/ -L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_highgui
ビルド成功後に以下のコマンドで実行させたのですが、
./opencv_test
画面描画関連のエラーが発生したので、以下のパッケージをインストールして問題解決しました。
sudo apt install libgtk2.0-dev
sudo apt-get install libcanberra-gtk-module
仕切り直しで実行すると、Opencv_run_200712.png今度は問題なく動作しました。次回は共有ライブラリの作成についてお話します。

ご訪問者数

(Since 24 July, 2016)

タグクラウド


プロフィール

Dr.BobT

Author: Dr.BobT
興味のおもむくままに生涯考え続けるエンジニアでありたい。

月別アーカイブ

メールフォーム

名前:
メール:
件名:
本文: