ねののお庭。

かりかりもふもふ。

OpenCV4で追加されたG-APIについて。

お品書き

  • OpenCV4新機能、変更点ざっくりまとめ
  • G-APIとは
  • とりあえずコードを見る。
  • 動かす。
  • 感想

OpenCV4.0.0リリース!

めでたい。

新機能とか変更点

OpenCV4.0になって、いろいろ変更とか追加があったので詳細は公式 を読むのが一番詳しいですが本流のほうだけざっくり

  • OpenCVC++11なライブラリになって、CのレガシーなコードはAPIの多くがは削除したよ!(includeにはopecv2しか入ってない)
  • DNN(深層学習ってやつ)の諸所が改善されているよ!(これはOpenCV3でcontribeからdnnモジュールが本流(?)に移ってから引き続きという感じ)
  • OpenCLが無理な環境用にVulkanを実験的に導入
  • パフォーマンス改善!(アプデ毎に高速化されてるし凄すぎて怖い)
  • QRコード検出とデコーダが乗っかった(地味にうれしい)
  • G-API追加!

G-APIってなんだ!!

OpenCV4から完全に新しく実装されたG-APIとかいうやつがいるのです。コイツは画像処理をグラフでとらえてノードとノードつなげて処理しようぜ!という感じのもの。ノードの分岐もよしなに出きるっぽい。 詳しい話はここ に書いてあるのですが、書いてあることざっくりまとめると

  • 実験的API(ここ大事)
  • 4.0.0リリースの段階で開発途中である(4系の内に完成させるつもりみたい)
  • 手続き的じゃなくて式的に処理を宣言して画像処理ができたりする。
  • バックエンドはCPU onlyじゃなくてちゃんとGPUも環境によっては回せるみたい(GPUは実験段階)。
  • メモリがいい感じ(雑)

って感じなのですが、手続き的じゃなくて式的なのが重要なのかなって感じがしますね。(最近の流行りですねぇ) とりあえず次にソースみますが、tensorflowとかいじったことあれば分かると馴染みがある感じだと思います。

「あ~ノードとノードつないでるぜ~」

みたいな感覚が得られます。

サンプルコード眺めてみる。

一番シンプルなサンプル眺めてみます。

#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/gapi.hpp>
#include <opencv2/gapi/core.hpp>
#include <opencv2/gapi/imgproc.hpp>

int main(int argc, char *argv[])
{
    cv::VideoCapture cap;
    if (argc > 1) cap.open(argv[1]);
    else cap.open(0);
    CV_Assert(cap.isOpened());

    cv::GMat in;
    cv::GMat vga = cv::gapi::resize(in, cv::Size(), 0.5, 0.5);//640x480に変換
    cv::GMat gray = cv::gapi::BGR2Gray(vga);//グレイスケールに変換して
    cv::GMat blurred = cv::gapi::blur(gray, cv::Size(5, 5));//グレースケールのがぞをぼかして
    cv::GMat edges = cv::gapi::Canny(blurred, 32, 128, 3);//エッジ検出
    cv::GMat b, g, r;
    std::tie(b, g, r) = cv::gapi::split3(vga);//カラー画像を3チャンネルに分割して
    cv::GMat out = cv::gapi::merge3(b, g | edges, r);//各チャンネルをマージ. |はgとエッジでビット単位でのor演算。
    //|はリファレンスには書いてないけどopencv2/gapi/operatos.hppに宣言されてた。ここら辺はMatとたぶん同じなのでしょう。
    cv::GComputation ac(in, out);//上記の流れをまとめて実行可能なグラフの実態を作成。

    cv::Mat input_frame;
    cv::Mat output_frame;
    CV_Assert(cap.read(input_frame));
    do
    {
        //グラフにデータを流してやる。
        ac.apply(input_frame, output_frame);
        cv::imshow("output", output_frame);
    } while (cap.read(input_frame) && cv::waitKey(30) < 0);

    return 0;
}

実行結果は以下です。一応動画撮りましたがまぁ想像通りだと思いますw

雑にコメント入れておきました。 簡単ですね。非常に簡単です。 ノード結んで処理書いてますって感じ。 C++11ライクなのでstd::tieとかで良しなに受け取っていますね。C++11になるまでだとvectorを作ってから引数に投げる,みたいな形で受け取っていたので非常に良いです。

ちなみにこれがどれだけ良さみの深い書き方かというのを示すため、愚直に手続き的に書いてみます。

void sameProcess(const cv::Mat& input, cv::Mat& output)
{
    using cv::Mat;
    Mat vga;
    cv::resize(input, vga, cv::Size(input.cols / 2, input.rows / 2));
    Mat gray;
    cv::cvtColor(vga, gray, cv::COLOR_BGR2GRAY);
    Mat blurred;
    cv::blur(gray, blurred, cv::Size(5, 5));
    Mat edges;
    cv::Canny(blurred, edges, 32, 128, 3);
    std::vector<Mat> rgbPanels;
    cv::split(vga, rgbPanels);
    rgbPanels[1] = rgbPanels[1] | edges;
    cv::merge(rgbPanels, output);
}

グラフのほうが分かりやすいですね...?

ちなみにOpenCV4になってCのAPIがいろいろ削除されたので、グローバルにあった

CV_BGR2GRAY

みたいな定数は使えなくなって、cv空間にある

cv::COLOR_BGR2GRAY

を使うことが強制されるようになりました(これ自体はOpenCV3の時すでにありました)。なので、

cvtColor()

などを使うときは気を付けましょう。

ちなみに速度差は対して出ませんでした。

感想

G-APIシンプルにシュと書けるので結構よさそうだなって思った(小並感) cv::gapiの中身とか読むと、あ~C++11だ~ええんじゃ~みたいお気持ちになれるのでよろしい。

dnn系のフレームが画像処理の今の最先端ではありますが、OpenCVはやはり好きです。