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に種々の機能追加をしていこうと思います。
スポンサーサイト



Azureのサブスクリプションを変更する

 Azureの30日間の無償期間(22,500円のクレジット付)もあっという間に終わり、無料試用版のサブスクリプションを変更するか、Azureを利用しないかの判断の必要が出てきました。AWSの試用の時もそうでしたが、1か月ってすごく短いですよね。平日は仕事なので実質は土日しかAzureに触れません。正味1週間程度しか味見ができす、当然クレジットもほとんど減っていない状態です・・。

 30日過ぎてクレジットは使えませんが、Azureでは12ヶ月間無償のサービスやいつでも無償のサービスもありますので、利用頻度が少ない中で、なるべく費用がかからないようにサブスクを変更することにしました。

 とりあえずは、12ヶ月間無償のサービスの中身を確認すると、12種類のAIサービスが利用できそうです。前回お話したTranslatorも含まれています。また、仮想マシンは750時間までで、一番パフォーマンスの低い「B1s」の仮想マシンは利用できます。B1sは「1個のVCPUと1GBのメモリ」のスペックです。使い物になるのだろうか疑問だったので、他の仮想マシン環境との比較・評価をしてみました。Azure-free-plan_201011.png この30日間はWindows10 Proの仮想環境(B2ms)も使っていたので、この仮想「PC」環境で評価することにしました。AzureのVM環境を3種類(B1s、B2s、B2ms)と以前のブログでお話したMacBook Airで構築したVirtual Box環境も加えた4つの環境で「行列積計算」をさせて処理時間を計測し、評価しました。行列積の計算プログラムは、ネット上にあったものをそのまま利用させていただきました。ありがとうございます。500行500列の2つの行列の積を取るプログラムで、10回計算して、その各回の処理時間を求めて最終的に平均処理時間を算出します。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;

namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
// Matrix size
var sizeA = 500;
var sizeB = 500;
// Create matrix
var matrixA = CreateMatrixData(sizeA, sizeA);
var matrixB = CreateMatrixData(sizeA, sizeB);
var normalTimes = new List<double>();

// Calculation (10 times)
for (var i = 0; i < 10; i++)
{
normalTimes.Add(MatrixProductNormal(matrixA, matrixB));
}
Console.WriteLine("Normal(Average):" + normalTimes.Average());
}

// Calculation of matrix product
public static double MatrixProductNormal(double[,] matrixA, double[,] matrixB)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
double[,] product = new double[matrixA.GetLength(0), matrixB.GetLength(1)];

for (int i = 0; i < matrixA.GetLength(0); i++)
{
for (int j = 0; j < matrixB.GetLength(1); j++)
{
for (int k = 0; k < matrixA.GetLength(1); k++)
{
product[i, j] += matrixA[i, k] * matrixB[k, j];
}
}
}

stopwatch.Stop();
Console.WriteLine("Measurement Time: " + stopwatch.Elapsed.TotalSeconds + "s");
return stopwatch.Elapsed.TotalSeconds;
}

// Create matrix data
public static double[,] CreateMatrixData(int rows, int cols)
{
double[,] matrix = new double[rows, cols];
Random r = new Random();

for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
matrix[i, j] = r.Next(100);
}
}
return matrix;
}
}
}
 結果の詳細は以下の通りです。Azure-VM_comparison1_201011.pngまとめると以下の通りです。各CPUのベンチマークを参考までに記入しました。Azure-VM_comparison2_201011.pngAzureのB2sとB2msはCPUが同じスペックのため、計算時間はほぼ同じ結果でした。B1sはわずか0.2secだけ遅い結果でしたが、メモリを多く使うプログラムでは有意な差が出るかもしれません。MacBook AirからのVirtual Box環境下のみ大きく引き離されましたが、5年以上前のマシンでCPUのキャパ不足であることがよく分かりました。

 いろいろな仮想マシンの味見も出来ましたので、サブスクのアップグレードを行いました。更新前は「無料試用版」でしたが、Azure-Subscription_before_201011.pngアップグレードボタンを押し、「テクニカルサポートなし」の無料を選択すると、Azure-Subscription_upgrade_201011.pngサブスクも切り替わりました。Azure-Subscription_after_201011.png 使用頻度もそれほどないので、最低スペックの仮想マシンB1sのままにしておこうと思います。仮想マシンの切り替えは自由に出来ますし、切り替えてもSSDのディスク中身は何も変わらないので、処理が遅いときは、費用がかかっても仮想マシンのグレードを上げて利用した方が良いかもしれません。微々たる金額ですしね。

AzureのTranslatorを使う(1)

 仕事の関係で最近MicrosoftのAzureを使うことがあり、アンチマイ○ソソフトの私ですが、「1ヶ月の無料試用版」で仮想マシン(Windows Server 2019やUbuntu 18.04等)を立てたりしていろいろ触っています。AWS、Azure、GCPのクラウドサービスの中で興味があるのは、データ解析等のサービスをAPIを使って簡単にできそうな所ですね。各社の実力の詳細は良く分かりませんが、日々進化しているので、ウォッチしておく必要はありそうです。

 今回は、AzureのCognitive Servicesの中の翻訳サービスである「Translator」を使ってみましたので、紹介します。Azureのユーザ登録や初期セットアップはまたの機会にお話します。

 Azureの検索窓に「translator」を入力して検索します。Cognitive Servicesの中のMarketplaceの中の「Translator」をクリックします。Translator_setup1_201004.pngTranslatorの作成画面の「基本」が表示されます。
 リソースグループは今回、新規作成で「cognitive-services-translator」としました。
 リソースグループ、リソースのリージョンは「東日本」としました。Azureのサービスの中には日本国内のリージョンでサポートしていないサービスもありますので、事前確認が必要です。Translatorはどのリージョンでもサポートしていました。
 名前は、リソースグループと異なる名前をつける必要があるので、「azure-translator」としました。
 価格レベルはフリープランの「Free F0」を選択しました。月あたり2M(200万)文字までの翻訳は無料なので、普通に困らない程度に使えそうですね。「次:タグ>」ボタンを押します。Translator_setup2_201004.png「タグ」画面が出ますが、そのまま「次:確認および作成>」に進みました。Translator_setup3_201004.png「確認および作成」画面で、検証が終わるまでしばらく待ちます。終了後、「作成」ボタンがアクティブになったら、ボタンを押しました。Translator_setup4_201004.pngデプロイが始まり、完了するまで待ちました。完了後、「リソースに移動」ボタンを押しました。Translator_setup5_201004.pngリソースグループに登録されていることが確認できました。「cognitive-services-translator」をクリックしました。Translator_setup6_201004.pngリソース「azure-translator」がリソースグループ「cognitive-services-translator」に登録されていることが確認できました。「azure-translator」をクリックしました。Translator_setup7_201004.pngクイックスタート画面が表示されました。ドキュメント等の使いかたを説明している画面です。左メニューの「キーとエンドポイント」をクリックしました。Translator_setup8_201004.png実際のアクセスに必要なキーが表示されます。キーは2つありますが、そのうちの1つを後ほどコード上に記入して利用します。Translator_setup9_201004.pngこれで、Azure側の準備と確認は終わりました。

 次に、実際に動かすコードを作成しますが、「チュートリアル:Azure Cognitive Services を使用して Flask アプリを作成する」を参考にして「テキストを翻訳する」までを実行させました。
 ユーザ側で変更した箇所は6行目のユーザキー(先程の2つのキーのうちの1つをコピー)と21行目のリソースを作成したリージョン名です。このリージョン名はデフォルトで「米国西部」設定になっていますので、コード上で変更しなければ、正常に動作しませんでした。ちなみにこの翻訳サービスは仮想マシン自身とは関係のないサービスなので、仮想マシンを起動させなくても利用可能です。
# translate.py
import os, requests, uuid, json

# Don't forget to replace with your Cog Services subscription key!
# If you prefer to use environment variables, see Extra Credit for more info.
subscription_key = '******** user key1 or key2 ***********'

# Don't forget to replace with your Cog Services location!
# Our Flask route will supply two arguments: text_input and language_output.
# When the translate text button is pressed in our Flask app, the Ajax request
# will grab these values from our web app, and use them in the request.
# See main.js for Ajax calls.
def get_translation(text_input, language_output):
base_url = 'https://api.cognitive.microsofttranslator.com'
path = '/translate?api-version=3.0'
params = '&to=' + language_output
constructed_url = base_url + path + params

headers = {
'Ocp-Apim-Subscription-Key': subscription_key,
'Ocp-Apim-Subscription-Region': 'japaneast',
'Content-type': 'application/json',
'X-ClientTraceId': str(uuid.uuid4())
}

# You can pass more than one object in body.
body = [{
'text' : text_input
}]
response = requests.post(constructed_url, headers=headers, json=body)
return response.json()


 実際に、Flaskアプリを以下のコマンドで実行し、
flask run
ローカルIP(http://127.0.0.1:5000/)に接続しました。英文を入力し、翻訳言語を「Japanese」にして実行すると、結果が出力されました。Translator_run_201004.png 翻訳する英文として、①メール記事と②PyTorch書籍の緒言部分の2種類を試してみました。Azure-TranslatorとGoogle翻訳の結果も比較し、主観的に良い訳の方に黄色でマーキングしました。
①メール記事
Dear friends,

Earlier this week, I asked a question on social media: What is the most important problem that the AI community should work on?
Thousands of you responded. The most frequently mentioned themes included:
Climate change and environmental issues
Combating misinformation
Healthcare including Covid-19
Explainable and ethical AI
Thank you to each person who responded. I have been reading and thinking a lot about your answers.
Many of the most pressing problems, such as climate change, aren’t intrinsically AI problems.
But AI can play an important role, and I’m encouraged that so many of you want to do good in the world.
Each of us has a role to play. But we rarely succeed alone. That's why community matters.
To my mind, the defining feature of a community is a shared set of values.
The medical community prioritizes patients' wellbeing.
When one doctor meets another, their shared priorities immediately create trust and allow them to work together more effectively, say, consulting on complex cases or building initiatives to help underserved people.
The academic community also has a history of collaboration stemming from its shared belief in the value of searching for and disseminating knowledge. So, too, in other fields.
②PyTorch書籍
When we started the PyTorch project in mid-2016, we were a band of open source hackers who met online and wanted to write better deep learning software.
Two of the three authors of this book, Luca Antiga and Thomas Viehmann, were instrumental in developing PyTorch and making it the success that it is today.
Our goal with PyTorch was to build the most flexible framework possible to express deep learning algorithms.
We executed with focus and had a relatively short development time to build a polished product for the developer market.
This wouldn’t have been possible if we hadn’t been standing on the shoulders of giants.
PyTorch derives a significant part of its codebase from the Torch7 project started in 2007 by Ronan Collobert and others, which has roots in the Lush programming language pioneered by Yann LeCun and Leon Bottou.
This rich history helped us focus on what needed to change, rather than conceptually starting from scratch.
①の翻訳結果は以下の通り。Translator_result1_201004.png②の翻訳結果は以下の通り。Translator_result2_201004.png 翻訳の微妙な表現(ニュアンス)は個人の好みによる所があると思います。Azure-translatorにしても、Google翻訳にしても個人的には甲乙つけがたい結果でした。ただ、あえてどちらかというと、Google翻訳の方が無償ながらクオリティが高い気がしました。今後も翻訳分野は進化していくと思いますので、引き続きウォッチしていきたいと思います。

ご訪問者数

(Since 24 July, 2016)

タグクラウド


プロフィール

Dr.BobT

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

月別アーカイブ

メールフォーム

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