読者です 読者をやめる 読者になる 読者になる

銀の弾丸

プログラミングに関して、いろいろ書き残していければと思っております。

OpenCVの画像処理をお手軽に ― OpenCVフィルター処理ライブラリ cvImagePipeline のご紹介

OpenCV 画像処理 C / C++

OpenCVの画像処理をお手軽にできるC++のフィルター処理ライブラリ cvImagePipeline のご紹介。

cvImagePipeline は、OpenCVの画像処理関数をモジュール化したC++向けのライブラリです。

画像処理の流れや各モジュールのパラメータの指定をXMLで記述できるようにしています(コードからも可能です)。

このため、画像処理の処理順の変更やパラメーター調整作業がはかどります。

背景除去のサンプルプログラム

以下の画像は、ライブラリに含まれているサンプルプログラムの実行中画面です(必要以上にテカっているのはワタシの額w)。

f:id:takamints:20161228110352p:plain

プログラミング言語C++ 第4版
SBクリエイティブ (2016-02-04)
売り上げランキング: 10,392

単純な背景除去の実装実験を行っている画面です。 入力画像や前処理中の画像などを、6分割した画面に統合して表示しています。 (カメラ入力や画面の分割、Window表示もそれぞれ、このライブラリのフィルターです。)

左上から順に、

  1. キャプチャ画像(の鏡像)
  2. 1をグレイスケールに変換
  3. 2に対して、ヒストグラム均一化とガウシアンブラーを適用
  4. 3の過去18000フレーム分を平均
  5. 3と4の差分(絶対値差分)
  6. 5の二値化

このあと、6の二値化画像に対して、膨張(dilate)と縮小(erode)を適当に繰り返してから元画像(1の画像)をマスクすれば背景と前景に分離できそうです。

処理は全てsample.xml(↓)に記述しており、参考として下に掲載しているC++のコード(capture.cpp)で読み込んで処理させています。 このためビルド無しで処理を変更可能です。(XMLを読み込む処理は複数のフィルターをまとめるImgProcSetの機能です)

sample.xml - 背景除去するXML

<cvImagePipeline name="testProcessor">
  <Processor class="VideoCapture" name="cap">
    <Property name="deviceIndex" value="0"/>
  </Processor>
  <Processor class="Flipper" name="fripHoriz">
    <Property name="flipDir" value="1"/>
  </Processor>
  <Processor class="ImagePoint" name="raw"/>
  <Processor class="ColorConverter" name="grayscale"/>
  <Processor class="EqualizeHist" name="equalizeHist"/>
  <Processor class="GaussianBlur" name="blur"/>
  <Processor class="DepthTo32F" name="depth32F"/>
  <Processor class="ImagePoint" name="pp"/>
  <Processor class="RunningAvg" name="background"> 
    <Property name="averageCount" value="18000"/>
  </Processor>
  <Processor class="AbsDiff" name="diff" autoBind="false">
    <Input to="src1" from="pp"/>
    <Input to="src2" from="background"/>
  </Processor>
  <Processor class="Convert" name="to8UC">
    <Property name="rtype" value="0"/>
    <Property name="alpha" value="255"/>
    <Property name="beta" value="0"/>
  </Processor>
  <Processor class="Threshold" name="binary">
    <Property name="type" value="CV_THRESH_BINARY"/>
        <!--
   CV_THRESH_BINARY
   CV_THRESH_BINARY_INV
   CV_THRESH_TRUNC
   CV_THRESH_TOZERO
   CV_THRESH_TOZERO_INV
   -->
    <Property name="otsu" value="1"/>
    <Property name="thresh" value="50"/>
    <Property name="maxval" value="255"/>
  </Processor>

  <Processor class="FitInGrid" name="integratedImage" autoBind="false">
    <Property name="width" value="960"/>
    <Property name="height" value="480"/>
    <Property name="cols" value="3"/>
    <Property name="rows" value="2"/>
    <Property name="interleave" value="0"/>
    <Input to="0" from="raw"/>
    <Input to="1" from="grayscale"/>
    <Input to="2" from="blur"/>
    <Input to="3" from="background"/>
    <Input to="4" from="diff"/>
    <Input to="5" from="binary"/>
  </Processor>
  <Processor class="ImageWindow" name="window">
    <Property name="windowName" value="cvImagePipeline"/>
    <Property name="showFPS" value="1"/>
  </Processor>
</cvImagePipeline>

capture.cpp - XMLを処理するプログラム

#include "stdafx.h"
#if defined(_MSC_VER)
#include <windows.h>
#else
#include <unistd.h>
#define Sleep(millisec) usleep(millisec * 1000)
#endif
#include <opencv2/opencv.hpp>
#include "cvImagePipeline.h"

using namespace cvImagePipeline;
using namespace cvImagePipeline::Filter;

#if defined(_MSC_VER)
int _tmain(int argc, _TCHAR* argv[])
#else
int main(int argc, char* argv[])
#endif
{
    cvInitSystem(argc, argv);
    ImgProcSet processors;
    std::string xml_filename("sample.xml");
    if (argc > 1) {
        xml_filename = argv[1];
    }
    if (!processors.loadXml(xml_filename)) {
        std::cerr << "ファイル読み込み失敗 ファイル名:"
            << xml_filename << std::endl;
        return -1;
    }
    Sleep(2000);
    while(true) {
        processors.execute();
        int ch = cvWaitKeyEx(1);
        if (ch == '\x1b') {
            fprintf(stderr, "exit.\n");
            break;
        }
    }
    cvDestroyAllWindows();
    return 0;
}

リポジトリ

上の例も含めて、その他詳細は下記リポジトリのREADMEを参照してください。

実を言うと、このリポジトリ、1年以上放置しているんですねえ。 しかし今でも Clone してくださる方が月に数人いらっしゃるようです。 READMEが古くなっていて申し訳ないですから、これを機にキチンとメンテしようと思ってはいますが・・・。

github.com

そういや以下の記事でもシレッと使っていますねw

takamints.hatenablog.jp

takamints.hatenablog.jp

今後の課題

今考えつく今後の課題は以下の様なことです。放置している場合ではないなあ。

  • OpenCV 3.0以上での動作確認。
  • Windows 10 での動作確認。
  • 基本フィルタと他のサンプルフィルタの分離。
  • 追加実装のしやすさを追求。
  • XMLXAML的文法に変更(記述量を少なくできそう)。
  • XMLのビジュアルな編集。
  • デバッグ機能の充実。
  • 実行中のフィルタのバイパスやパラメータの変更機能。
  • Pythonから利用できるインタフェース。C++はやっぱり敷居が高いかも。
  • 妙に凝った変な名称のフィルタを改名するw