Rest Term

Blog

OpenCV 4.0のQRコード検出、G-APIを試す

先日、OpenCV 4.0が正式リリースされました。リリースノートの内容を載せます。

* OpenCV is now C++11 library and requires C++11-compliant compiler. Minimum required CMake version has been raised to 3.5.1.
* A lot of C API from OpenCV 1.x has been removed.
* Persistence (storing and loading structured data to/from XML, YAML or JSON) in the core module has been completely reimplemented in C++ and lost the C API as well.
* New module G-API has been added, it acts as an engine for very efficient graph-based image procesing pipelines.
* dnn module was updated with Deep Learning Deployment Toolkit from the OpenVINO™ toolkit R4. See the guide how to build and use OpenCV with DLDT support.
* dnn module now includes experimental Vulkan backend and supports networks in ONNX format.
* The popular Kinect Fusion algorithm has been implemented and optimized for CPU and GPU (OpenCL)
* QR code detector and decoder have been added to the objdetect module
* Very efficient and yet high-quality DIS dense optical flow algorithm has been moved from opencv_contrib to the video module.

まずは概要を確認すると、OpenCV4.0では実行環境がモダンになっていて(とはいえそれほど新しくはないのですが)、多くのC言語のインタフェースがついに削除されたようです。下位互換は保証されてないので業務で利用する際には注意が必要そうです。機能面ではGraph API (G-API)やDNNモジュールのIntel Deep Learning Deployment Toolkit (DLDT)連携、ONNXフォーマット対応など、近年のエッジデバイスにおける機械学習ブームを意識しているように見えますね。また、非機能面においても多くの性能改善が施されているようです。Intel CPUならAVX2命令をより活用するようになったりと、幅広い環境で高いパフォーマンスを発揮できるような細かな改善が見られます。さすが、メジャーバージョンが上がっただけのことはあります。

環境

* CentOS 7.5 (x86_64/Xeon 8core CPU/16GB RAM)
* GCC 4.8.5
* CMake 3.12.2
* Python 3.6.5 (Anaconda custom)

いつものようにLinuxサーバ環境上に構築しました。今回はGPU(CUDA)環境は無しで試します。

インストール

OpenCV3.x系と比べて環境に対する要求がいくらか上がっているようなので注意してください。特にC++11以降が必須なのが大きいかなと思います。まずは今回の環境で必要なパッケージ群を予めインストールしておきます。Python関連はAnaconda環境を構築した段階で揃うはずなのでここでは割愛。また、Linuxサーバ環境にインストールするので各種GUI環境も利用しません。

*-develパッケージの方を入れると依存パッケージも大体一緒に入れてくれるはず。UbuntuとかのDebian系なら*-devパッケージでOK。

僕の場合はpyenv環境上でPythonでの開発を普段行なっているので、OpenCVのPythonインタフェースもpyenv環境に適用します。それ以外だとIntel TBB利用を明示的に指定しているくらいです。各画像コーデック/デコーダ、Intel IPP や Protocol Buffer 等のサードパーティライブラリはCMake側でインストールチェック、デフォルトで有効化してくれますので、ごちゃごちゃとオプションを細かく指定する煩雑さは軽減されているようです。GPU(CUDA)環境を利用する場合はもう少し設定が増えて複雑になりますけど今回は利用しませんので。

ただし、2018/12現在、環境によってはLAPACKのリンクをオフにする必要があるようです。CMakeオプション WITH_LAPACK=0 にすれば良いです。GitHub issueにも挙がっています。

動作確認

せっかくなのでOpenCV4.0から追加された新機能を試してみます。目玉機能の一つとして注目度の高いG-APIを触る前に、まずは気軽に使えそうなQRコード検出機能を試してみました。

QRコード検出

入力画像として以下のQRコード画像を使います。このブログのURL(https://rest-term.com)情報を埋め込んで作りました。

まずはC++インタフェースから試してみます。

* qrcode_sample.cpp

OpenCVのAPIは名前空間を省略せずに書いたので、cv::が付いている箇所を中心に見てみると少ないコードでQRコード検出できることがわかります。検出器のインスタンスを作って入力画像や出力結果を入れるコンテナを渡すだけです。C++11以降で使える型推論等の機構は使ってませんが、この規模のコードだとそれほど効果的ではないと思います。

* CMakeLists.txt

CMakeの最小バージョンは一応OpenCV4.0本体と合わせておきました。あとはC++11以降でコンパイルすることに注意しておけば大丈夫そうです。

* 実行結果

* 出力画像

正しくQRコードを検出できていますし、URL情報のデコードにも成功しています。QR決済のシステムやアプリに組み込むならデコード処理だけで良さそうです。また、QRコードの規格にはバージョンというものがありまして、QRコードの大きさはバージョン1の21×21セルからバージョン40の177×177セルまで4セル刻みで決められています。cv::QRCodeDetector::detectAndDecodeの3つめの引数にバージョンに合わせた解像度(セル数)でQRコードを書き出してくれるのでバージョン番号も計算できます。今回作成したQRコードだとバージョン3(29x29セル)だということがわかります。

次はPythonから使ってみます。PYTHONPATHは予め通しておきます。

* qrcode_sample.py

* 実行結果 (出力画像はC++版と同じなので省略)

Pythonインタフェースも動作に問題なさそうです。パッケージが cv2 なのはそろそろ変えて欲しいところですけども。

Graph API (G-API)

次はG-APIを試してみます。一連の処理を計算グラフとして構築・適用する機構となっています。DNNフレームワークだと一般的に備わっている仕組みで、Web系バックエンドでもよく利用されるApache Sparkのような分散処理環境においても同様の考え方で処理を記述することができます。膨大なロジックであってもグラフ上での局所的な計算の繋がり(組み合わせ)として考えるので、途中計算結果を共有できる(システム的にはキャッシュとして扱える)のはスループット向上に繋がりそうです。

公式のG-APIのチュートリアルはビデオ入力を伴っているので、ここでは少し修正して入力・出力共に静止画像を扱うようにしました。

* 入力画像

* 出力画像

個々の画像処理を繋げてパイプラインを作っていることが読み取れます。このサンプルでは一連の処理をすぐに適用していますが、適用自体は結果が必要になる時まで遅らせることも出来ますし、実行時に途中の処理を他の処理に差し替えることもできます。途中の処理結果はconstにしていることからもわかるようにイミュータブルなオブジェクトとして扱うことができるのも嬉しいですね。適切な粒度でグラフを構築しておけば、データの不変性・局所性により、どこかのノードで計算処理に失敗した場合でも、処理が成功しているノードまで遡って続きから再実行することもできます。

定義した計算グラフを遅延評価したい場合は、cv::GComputation::applyの代わりにcv::GComputation::compileでグラフを事前構築してから、任意の場所で実行できます。

また、2018/12現在、G-APIのPythonインタフェースは提供されていないようです。G-APIのC++インタフェースは、例えばApache SparkのRDD/DataFrame/Datasetを活用したコードと比較するとまだ洗練されていないように見えますし、そのままPython用にバインディングしてもスクリプト言語らしい手軽さで使えるようにするのは難しいかもしれません。

一応、グラフ定義部分はラムダ式を使うと以下のようにも書けます。一時オブジェクトのスコープが限定されるところは良いですけど、あんまり変わらない気も。

また、G-APIの重要な機構のひとつに実行環境を透過的に選択できる点があります。2018/12現在、リファレンスを見る限りCPU/GPU(via OpenCL)/Fluidがサポート予定のようです。今回はCPU環境で試しましたが、次はGPU(CUDA)環境にインストールしていろいろ試す予定なので、G-APIの実行環境についてはまた別記事にしようと思います。

なんだかんだ書きましたけど、G-API、とても面白いです。OpenCV4.0時点ではまだ開発段階とのことなので今後の改善に期待したいです(まぁ僕自身がOpenCV開発に貢献できればもっと良いのですが)。

おわりに

OpenCV2.x系は仕事でもよく使ってたのですが、OpenCV3.x系は結局ほとんど使うことなく4.0を試すことになってしまいました。3.x系からの機能差分や旧バージョンとの非互換性等の整理をしておきたいところです。メジャーバージョンが上がっただけあって、多くの新機能追加、性能改善などが施されているようですので引き続きいろいろ試してみたいと思います。

今回作成したサンプルコードはGitHubにも上げてありますので興味があれば。

先月まで仕事が激務だったんですが、やっと余裕ができたのでブログ更新の頻度も上げられるといいなぁ。

 

Tags: , ,

GitHubのユーザープロフィールを表示するWordPressプラグインを作った

WordPressの勉強目的にプラグインを1つ作ってみました。

WP GitHub Card

GitHubのユーザープロフィールをカード形式でWordPressのページに貼り付けるだけのプラグインです。以下のように使います。

上記のコードをWordPress上の任意の場所に貼り付けます。WordPressのショートコード形式となっているのでウィジェットに限定しません。どこでもOKです。

* 表示例

上記スクリーンショットの通り、以下の情報が表示されます。

  • GitHubアカウント名とアバター画像
  • 最近コミットしたリポジトリ
  • よく使うプログラミング言語トップ3(リポジトリのスター数でソート)
  • パブリックなリポジトリとgist数
  • フォロワー数
  • 全てのリポジトリのスター数の合計

データ取得にはGitHubの公式APIを利用しており、取得した各種データはWordPressのDB内に一定時間キャッシュして非機能面も考慮していまう(Transientsのキャッシュ機構を利用)。

公式プラグインページとソースコードは以下にあります。

目的

元々はGitHub Badgeというサービスの機能を自分のサーバでホスティングしたいという動機からプラグイン開発を始めました。GitHub Badgeのように外部コンテンツをiframeで自分のサイトに埋め込む方式だと性能面やセキュリティ面で問題を感じており、また、サービスとして提供すると不特定多数の人が利用するためGitHub APIのリクエスト数制限にもひっかかる頻度が高くなります。それらの問題を解決するために、自分のサーバでコンテンツ配信しつつ適切にデータをキャッシュすることにしたという経緯です。

WordPressについて

このサイトはWordPressで数年運用していますけど、これまでWordPress本体についての知識はほとんどなかったです。まぁプラグインを作った程度ではWordPressのほんの一部しか理解できないと思いますが、良い勉強になりました。

ところで2018年現在、インターネット全体(厳密にはtop10 millionのサイトが集計対象)の約30%のWebサイトがWordPress製サイトであるという統計結果があります。

2015年時点では25%程度だったそうなので、そこから3年弱で5%も上がったということですか。大昔にみんながMovable TypeからWordPressに移転してた頃に現在のこの状況を想像できたでしょうか。

また、GoogleもWordPressエキスパートの求人をしているようです。それほどWordPressは現在のWebにおいて重要な位置に鎮座しているということでしょうか。最近まで転職活動をしてた知人のWebデザイナーも面接でWordPressが使えるかどうか聞かれたことがあると言っていました。

長年WordPressを触っていたにもかかわらず、これまで全くと言っていいほどWordPressについて無知でした。遅いスタートになりますが今後もプラグインやテーマを作ってみる機会を増やしてWordPressの学びを増やしていきたいと思います。

 

Tags: , ,

常時SSL化とHTTP/2対応しました

ドメイン rest-term.com の常時SSL化とHTTP/2に対応しました。

本サイトのコンテンツはCloudflareで配信しているので常時SSL化自体は簡単でした。後はコンテンツ内でのhttp/https混在(mixed content)を解消したり、パフォーマンス改善のためにWordPressプラグインを整理したり、一部は自作したものに置き換えたりとか細かい作業をしていました。SSL化関連の作業メモについてはWikiの方に書いてますので興味があれば。CloudflareだけでなくLet's Encryptの証明書発行作業等についても触れています。

現在、RSSフィードは旧URLと新URL(https://rest-term.com/feed/atom/)の両方を配信してます。いずれは新URLの方だけにする予定です。

HTTPS化したのでHTTP/2対応も併せて行いました。早くHTTP/2が当たり前の世界になって欲しいですね。

HTTPS化するとはてなブックマークなどのSNSへのシェア数がリセットされる問題は有名かと思いますが、SNS Count Cacheプラグインを利用すればHTTP時代の数値を合算表示させるのは簡単に実装できました。あとはGitHubバッジのウィジェットが重いのでセルフホスティングできるタイプのプラグインを自作しようかと思っています。

近い内に各Webブラウザが非HTTPSサイトを警告表示する取り組みが実施されるかと思いますが、常時SSL化が間に合って良かったです。まだ一部不具合が残っているページもあるかもしれませんが確認次第対応していきます。

 

Tags: ,

PythonのWebアプリケーションフレームワーク Sanicを試す

PythonのWebアプリケーションフレームワークについて、Flaskからの移行先としてSanicが有力そうなので調べています。

Sanic is a Flask-like Python 3.5+ web server that's written to go fast.

Sanicは著名なイベントループライブラリであるuvloopを利用しており、Node.jsのように非同期I/Oによる高効率なHTTPリクエスト処理が可能です。また、SanicはFlaskとよく似たシンタックスを提供しているため、他のフレームワークよりも移行コストを抑えることができそうです。

環境

サーバ環境

  • Ubuntu 16.04 (x86_64)
  • CPU 3.30GHz 4core / 8GB RAM
  • Python 3.6.4
  • Sanic 0.7.0 / Flask 0.12

クライアント環境

  • macOS High Sierra 10.13.4 (x86_64)
  • CPU 1.70GHz 4core / 8GB RAM

ネットワーク環境

注意点としてはSanicは asyncio および async/await 機能を利用しているためPython3.5以降が必要です。Python3移行は2018年現在なら世間的にもけっこう進んでいるとは思いますが、3.5以降となると業務で使っている人はまだ少ないかもしれません。

使い方

公式でFlask-likeと謳っているとおりインタフェースはFlaskとよく似ているので移行作業自体は楽だと思います。

Flaskでhello, world

Sanicだと以下のようになります。まさにFlask-likeですね。

上記SanicのコードではPython3.5以降で正式に利用できるasyncキーワードが現れています。C#やNode.JSなど他言語におけるasync/awaitと同等の機能が言語標準で提供されるようになりました。Sanicを使う場合は、個人的にNode.js開発でよくやってしまうawaitの置き忘れも気にしなくて良いのは助かります。

JSON対応

JSONレスポンスを返すには以下のように書きます。FlaskもSanicどちらも使いやすいと思います。パッケージ構成はSanicの方が整理されてる気はしますがどうでもいいレベル。

ちなみにJSONパーサーは独自実装ではなく外部モジュールが使われています。Flaskを使う場合はsimplejsonをセットで入れておくと良いです。Sanicはujsonが必須となっています。

  • Flask: simplejson or json
  • Sanic: ujson

JSONモジュールのベンチマークに関しては以下のサイトが参考になります。

ujson優勢みたいなのでJSONレスポンスを含めたベンチマークだとFlaskが不利だと思うので、テキスト("hello,world")のみを返すシンプルなコードで計測します。ただ、有利不利とか言ってしまうとuvloopに乗っかっているSanicはズルいという話になってしまいますけども。。

ベンチマーク

前述のサンプルコード('hello, world'文字列を返却)で組み込みHTTPサーバのベンチマーク。同一LAN内でクライアント/サーバの2台用意して計測、ツールはwrk2を利用しました。

ネットワーク周りのカーネルパラメータは以下の通りです。TCPのsyn backlogをsomaxconnの値と合わせて増やす程度のチューニングはしてあります。TCP接続を使い回す設定にもしていますが、今回のベンチマークでは意味ないですね。

まずは4スレッド同時100コネクション、2000rpsで30秒間負荷を与えました。

レイテンシ周りの計測値を見てみるとわかりますが、これだけでもかなり性能差があることが読み取れますね。低スペックのサーバで動かしているのですがSanicの方はまだまだ余裕がありそうです。次は4スレッド同時200コネクション、4000rpsで30秒間負荷を与えました。

Flaskの方は限界が見えてますね。タイムアウトも発生しています。ネットワーク周りのカーネルパラメータが未調整だと他のソケット関連エラー等も併せて出てくるはずです。一方でSanicの方は安定した性能です。uvloopすごい。

(横軸の数値が見えてないですが、1/ (1 - Percentile) 刻みの対数表示となっています)

おわりに

Pythonのasyncioに対応したフレームワークはSanic以外にもいくつかありますが、既存のFlask製アプリケーションの移行コストを最小化するために、Flask-likeなSanicを採用するのは悪くない選択だと思います。2,3年前にFlaskの代替として、Falconというフレームワークに移行する作業もやったことがあるのですが、使い方がFlaskと結構違うので移行コストが高かったです。その点、Sanicを使えばその辺りの面倒を省くことができますね。Flaskプラグインの移植性も高く、ベストプラクティスの流用も行いやすいのではないでしょうか。

また、Sanicはasyncioのdrop-in replacement実装としてuvloopを組み込んでおり、uvloop自体はlibuvをベースにしているので実績も十分、性能も保証されています。Python3.5以降が必要なので実業務で採用するのはまだ少しネックになりそうですが、そこは時間が解決してくれる部分でしょう(スタートアップのサービスとかなら勢いで採用しても良いかも?)。他にuvloopをベースにしたフレームワークとしてはjaprontoというのもあります。Flask-likeでなくても良いならこちらも選択肢としてアリですね。

まずはプライベートワークで作っているPython製WebAPIはSanicに移行してみようと思いました。前回紹介した pyvips(libvips) を利用すれば高速な画像処理サーバもPythonで簡単に書けそうです。

参考

 

Tags: ,