OpenCV 3.4.1で背景差分
背景差分
画像の前景と背景を分離する手法。2013年にOpenCV 2.4.7での背景差分の記事を書いたが、2018年になったいまOpenCV 3.4.1で背景差分を行おうとしたら使えるアルゴリズムが増えていたのでまとめておく。
アルゴリズム
MOG, MOG2, GMG
OpenCV 2.4.7でも使えた。以下のページに日本語でわかりやすい説明がある。
背景差分 — OpenCV-Python Tutorials
KNN
K近傍方に基づく背景差分。前景の画素数が少ない場合は効率が良いらしい。
CNT
低スペックな計算機でもほかのアルゴリズムより高速に処理ができる。CNTという名前は「CouNT」の省略らしい。Raspberry Pi3でのベンチマークではMOG2が41秒に対し、CNTは18秒。
https://sagi-z.github.io/BackgroundSubtractorCNT/
LSBP
Local SVG Binary Pattern。注目画素とその周辺画素の大小関係を符号化するLBP(Local Binary Pattern)は、高速に計算可能な画像特徴量として知られている。局所的なノイズや隣接画素が類似しているような場合にもロバストにするようにSVD(特異値分解)を使った特徴量で背景差分を行っているそう。ちゃんと勉強してないのでよくわらからない。
GSOC
LSBP特徴を使ってる。ノイズ除去とか穴埋めといった後処理をしているらしい。GSOC(Google Summer of Code)2017中で開発されたとかで元論文等はないらしいのでソースコードを読もう。
コード
実行にはopencv_contribが必要。
C++
opencv_contrib
をダウンロードし、opencv
のcmake
時に例えば以下のようにOPENCV_EXTRA_MODULES_PATH
オプションを指定してビルドする。
$ cmake -DOPENCV_EXTRA_MODULES_PATH=<opencv_contrib>/modules -DBUILD_opencv_legacy=OFF <opencv_source_directory>
ソースコード
#include <opencv2/opencv.hpp> #include <opencv2/core/utility.hpp> #include <opencv2/bgsegm.hpp> int main() { // 動画ファイルの読み込み cv::VideoCapture cap = cv::VideoCapture("video.mp4"); // 背景差分器の生成 cv::Ptr<cv::BackgroundSubtractor> bgfs = cv::bgsegm::createBackgroundSubtractorGSOC(); //cv::Ptr<cv::BackgroundSubtractor> bgfs = cv::bgsegm::createBackgroundSubtractorCNT(); //cv::Ptr<cv::BackgroundSubtractor> bgfs = cv::bgsegm::createBackgroundSubtractorGMG(); //cv::Ptr<cv::BackgroundSubtractor> bgfs = cv::bgsegm::createBackgroundSubtractorLSBP(); //cv::Ptr<cv::BackgroundSubtractor> bgfs = cv::bgsegm::createBackgroundSubtractorMOG(); //cv::Ptr<cv::BackgroundSubtractor> bgfs = cv::createBackgroundSubtractorMOG2(); //cv::Ptr<cv::BackgroundSubtractor> bgfs = cv::createBackgroundSubtractorKNN(); while (cv::waitKey(1) == -1) { cv::Mat frame, foreGroundMask, segm; cap >> frame; if (frame.empty()) break; bgfs->apply(frame, foreGroundMask); frame.convertTo(segm, 0, 0.5); cv::add(frame, cv::Scalar(100, 100, 0), segm, foreGroundMask); cv::imshow("output", segm); } return 0; }
Python
pip
で管理している場合、以下のようにopencv-python
をアンインストール後にopencv-contrib-python
をインストールすればopencv_contrib
が使える。
pip uninstall opencv-python pip install opencv-contrib-python
ソースコード
import cv2 cap = cv2.VideoCapture('video.mp4') bgs = cv2.bgsegm.createBackgroundSubtractorLSBP() while(cap.isOpened()): ret, frame = cap.read() mask = bgs.apply(frame) bg = bgs.getBackgroundImage() cv2.imshow('mask', mask) cv2.imshow('bg', bg) if cv2.waitKey(1) != -1: break cap.release() cv2.destroyAllWindows()
適用結果
比較のために各アルゴリズムを並べてみた。パラメータ調整もしてないから比較もくそもない気はするけど。GSOCが綺麗。LSBPはあんまり
あと、背景差分やるだけだったらたぶん以下のページで紹介されているBGSLibraryを使ったほうが良さそうではある。
Python+OpenCVで特徴量記述・アルゴリズムまとめ
Python+OpenCVで画像の特徴点の特徴量を記述する.OpenCV2.4.9およびOpenCV3.0 alphaについて使用できるアルゴリズムをまとめる.
Python+OpenCVで特徴点抽出・使えるアルゴリズムまとめ OpenCV2.4.9と3.0 alpha - whoopsidaisies's diary
の続き.
特徴量記述器の使い方
特徴点抽出のときのFeatureDetectorと同じように,特徴量記述についてもDescriptorExtractorという共通インターフェースが用意されている.
使い方は以下の通り.
# 引数でどの特徴量記述子を使うか指定 extractor = cv2.DescriptorExtractor_create(extractor_name) # 引数として画像と特徴点を渡す # 戻り値は特徴点と特徴量 keypoints, descriptors = extractor.compute(img, keypoints)
DescriptorExtractor_createメソッドで指定出来る特徴量記述子を以下にまとめた.
OpenCVバージョン | 実数ベクトル | バイナリコード |
---|---|---|
2.4.9 | SIFT, SURF | BRIEF, BRISK, |
3.0 alpha | KAZE | AKAZE, BRISK, ORB |
(2.4.9でORBがなぜかランタイムエラーが出るので追々調査予定)
特徴量の中身
descriptorsの中身は2次元のarrayになっており,特徴点ごとに特徴量のベクトルが格納されている.
KAZEで記述した特徴量を表示してみるとこんな感じ(0番目の点).
>>descriptors[0] array([ -7.38840317e-04, -6.34267868e-04, 2.61819176e-03, 3.50267510e-03, -1.59906253e-04, -4.56994586e-03, 4.39605350e-03, 9.44076758e-03, -1.75689301e-03, 8.05667194e-04, 3.55383591e-03, 4.12554527e-03, -1.54193444e-03, 1.58668880e-03, 2.78847502e-03, 3.14337271e-03, -1.62520558e-02, 1.65848620e-02, 3.65739577e-02, 2.07063138e-01, 2.64649391e-02, -6.13974873e-03, 9.64385048e-02, 2.91850477e-01, 5.54781593e-02, 6.74537197e-03, 6.74520805e-02, 8.45865980e-02, -2.12559430e-03, 1.18403265e-03, 5.09992335e-03, 4.04977519e-03, -1.28035052e-02, 6.93068025e-04, 3.93203236e-02, 4.54802722e-01, -2.96720478e-04, 6.83288351e-02, 8.15773308e-02, 6.17026806e-01, -1.08468741e-01, 5.95125668e-02, 1.92962006e-01, 2.17324436e-01, -1.21422755e-02, 2.50244630e-03, 1.34693161e-02, 6.25377288e-03, 2.55310140e-03, -4.21544649e-02, 2.98030730e-02, 1.50349155e-01, 4.27789390e-02, -9.73087847e-02, 1.63991615e-01, 1.90487400e-01, 3.39032081e-03, -2.32313443e-02, 1.98113456e-01, 6.58137947e-02, -1.16498368e-02, 2.66303378e-03, 1.25482017e-02, 4.14769724e-03], dtype=float32)
グラフ表示だとこんな感じ.
import matplotlib.pyplot as plt plt.plot(descriptors[0])
KAZEは64次元の実数ベクトルで特徴量記述をするので以上のようになるが,記述子によってデータ量が違うため配列の長さや型が変わってくる.
Python+OpenCVで特徴点抽出・使えるアルゴリズムまとめ OpenCV2.4.9と3.0 alpha
軽量プログラミング言語が苦手なので敬遠していたが,世間ではPythonからOpenCVを呼ぶのが流行っているようなので,練習がてらOpenCVで使える特徴点抽出アルゴリズムをまとめてみる.
OpenCV2.4.9とOpenCV3.0 alphaについてまとめる.3.0 betaはなぜか動かなかったのでいつか暇があれば調査.
環境
WindowsでOpenCV公式サイトのダウンロードページから2.4.9および3.0 alphaのビルド済みバイナリをダウンロードしてきて解凍したものを使う.
PythonからのOpenCVの呼び出し方はこことかを参照.
特徴点抽出の使い方
OpenCVで実装されている特徴点抽出のアルゴリズムはいくつかあるが,FeatureDetectorという共通インターフェースを使うことでどのアルゴリズムでも同様の記述で使える.使い方は以下.
# 画像読み込み img = cv2.imread('gazou.bmp') # FeatureDetectorのインスタンスを生成 detector = cv2.FeatureDetector_create(detector_type) # 特徴点を抽出 keypoints = detctor.detect(img)
FeatureDetector_createメソッドの引数にアルゴリズム名を文字列で渡すことで,どのアルゴリズムで特徴点抽出するかを選択することが出来る.例えば,ORBアルゴリズムで特徴点を抽出して表示するのは以下のようになる.
import cv2 img = cv2.imread('gazou.bmp') # アルゴリズム名を引数で渡す detector = cv2.FeatureDetector_create('ORB') keypoints = detctor.detect(img) # 画像への特徴点の書き込み out = cv2.drawKeypoints(img, keypoints, None) # 表示 cv2.imshow(name, out) cv2.waitKey(0)
結果は以下のようになる.
ちなみに特徴点の書き込み部分を以下のようにすると,特徴点のサイズと方向を表す円が描画される.
out = cv2.drawKeypoints(img, keypoints, None, None, cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
FeatureDetectorインターフェースで使えるアルゴリズム
FeatureDetectorインターフェースで指定できる特徴点抽出アルゴリズムはOpenCVのバージョンで変わるようなので,OpenCV2.4.9で使えるものとOpenCV3.0 alphaで使えるものを以下にまとめる.
バージョン | アルゴリズム名 |
---|---|
2.4.9 | BRISK, Dense, FAST, FASTX, GFTT, HARRIS, MSER, ORB, SIFT, STAR, SURF, SURF_OCL, SimpleBlob |
3.0 alpha | AKAZE, BRISK, FAST, GFTT, HARRIS, KAZE, MSER, ORB, SimpleBlob |
C++版については以下の記事にまとめてある.
OpenCV3.0.0-alphaの特徴抽出・マッチングまとめ - whoopsidaisies's diary
OpenCVで画像の特徴抽出・マッチングを行う - whoopsidaisies's diary
Visual Studioでデバッグ時にOpenCVのMat等の画像を表示できるプラグインが便利
OpenCV公式サイトのNEWSを見てたら「Image Watch plugin for Visual Studio」というのを見つけた.使ってみたら便利そうだったのでメモ.もっと早く知りたかった.
わざわざimshowとかのコードを追加してウィンドウを表示させなくてもMatの中の画像を確認できる.また,拡大表示とか画素値の表示も出来るため通常のOpenCVの画像表示用のウィンドウより便利な気がする.
Image Watch plugin for Visual Studioのインストール
以下のサイトからダウンロードしてきて指示にしたがってインストールするだけ.
Image Watch extension
以下のサイトに詳しい手順等載ってます.
Image Watchの使い方 | dandelion's log
使ってみる
以下のコードで試してみた.画像を表示させるためのコードは特に必要ない.画像が入っている変数があれば自動で表示してくれる.
#include <opencv2/opencv.hpp> using namespace cv; void main() { // 画像読み込み Mat img = imread("Penguins.jpg"); // Sobelフィルタ Mat imgSobel; Sobel(img, imgSobel, img.depth(), 1, 1); // ラプラシアンフィルタ Mat imgLaplacian; Laplacian(img, imgLaplacian, img.depth()); // Cannyアルゴリズム Mat imgCanny; Canny(img, imgCanny, 50, 200); }
一番下の行にブレークポイントを置いて,デバッグ実行して止める.
「表示」メニューの中から「Image Watch」を探してクリックしてウィンドウを表示させる.
そうすると以下の様なウィンドウが出てきて,Mat型の変数とその中身の画像が表示される.
左側に画像一覧があるので,クリックすれば以下のようにその画像が表示される.
マウススクロールで拡大可能.
最大まで拡大すると各画素の画素値が数字で表示される.
画素を右クリックしたら,その画素のアドレスのコピーとかも出来るもよう.
以上のようにOpenCVのMatの中の画像を簡単に確認できるので,Visual StudioでOpenCVを使う場合はかなり便利だと思う.
試してないけど配布サイトの説明によると,cv::Mat_<>, CvMat, _IplImageなんかにも対応しているらしい.
opencvで文字認識その1 Tesseractラッパ
OpenCV3.0系から文字認識モジュールが搭載されるようなので使ってみる.現状の3.0 alphaや3.0 betaでは,文字認識モジュールはメインレポジトリに組み込まれておらず開発用レポジトリのopencv_contribの方に入っているようで,opencv_contribと一緒にOpenCVをビルドする必要がある.
OpenCVの文字認識モジュール
OpenCVのドキュメントによると,以下の2種類の文字認識方法があるらしい.
- オープンソースのOCRライブラリtesseract-ocrを呼び出す方法
- 隠れマルコフモデルによる認識方法
今回は,1の方法について試してみる.
文字認識モジュールの準備
tesseract-ocrのダウンロード
https://code.google.com/p/tesseract-ocr/downloads/listからVC++からtesseract-ocrを呼び出す用のライブラリをダウンロード・解凍する.「tesseract-3.xx.xx-win32-lib-include-dirs.zip」って名前のやつ.今回は「tesseract-3.02.02-win32-lib-include-dirs.zip」を使った.
https://code.google.com/p/tesseract-ocr/downloads/listからtesseract-ocr用の学習済みのデータファイルをダウンロードしておく.今回は英語で試すため「tesseract-ocr-3.02.eng.tar.gz」をダウンロード・解凍する.
opencv_contribのダウンロード
https://github.com/itseez/opencv_contribからopencv_contribをダウンロード・解凍する.
OpenCVのビルド
CMakeを使ってビルドする.ここらへんを参考に.
「OPENCV_EXTRA_MODULES_PATH」パラメータの値を「(opencv_contribのパス)/modules」にセットしてConfigureする.
すると「Tesseract_INCLUDE_DIR」と「Tesseract_LIBRARY」というパラメータが出てくるので
- Tesseract_INCLUDE_DIR ← (tesseract-ocrのパス)/include
- Tesseract_LIBRARY ← (tesseract-ocrのパス)/lib
とセットする.
以上の設定でGenerateしてOpenCVをビルドすると文字認識モジュールが組み込まれる.で,インストールする.
動作確認
実際に画像を入力してやってみたソースコードと結果が以下.OpenCVのMat形式の画像を使って文字認識出来るため,tesseract-ocrのAPI直接叩くよりは使いやすいと思う.
追記)文字のかたまりの位置の出力が間違っていたためソースコードと結果画像を修正した.
ソースコード
#include <opencv2/opencv.hpp> #include <opencv2/text.hpp> void main() { // 画像読み込み auto image = cv::imread("moji.jpg"); // グレースケール化 cv::Mat gray; cv::cvtColor(image, gray, COLOR_RGB2GRAY); // 文字認識クラスのインスタンス生成 auto ocr = cv::text::OCRTesseract::create("(言語データのパス)/tessdata", "eng"); std::string text; std::vector<cv::Rect> boxes; std::vector<std::string> words; vector<float> confidences; // 文字認識の実行 ocr->run(gray, text, &boxes, &words, &confidences); // 結果出力 printf("%s\n", text.c_str()); // 文字のかたまりごとに出力 printf(" 文字 | 位置 | 大きさ | 信頼度\n"); printf("-----------+------------+------------+----------\n"); for (int i = 0; i < boxes.size(); i++) { printf("%-10s | (%3d, %3d) | (%3d, %3d) | %f\n", words[i].c_str(), boxes[i].x, boxes[i].y, boxes[i].width, boxes[i].height, confidences[i]); } }
実行結果
OpenCVでDeepFlow
opencv_contribを見てたら,DeepFlowというオプティカルフローのアルゴリズムが実装されていたので勉強ついでに使ってみる.
DeepFlow
DeepFlowはコンピュータビジョンの分野では有名な国際学会ICCV2013でWeinzaepfelらによって発表されたアルゴリズムである.
HAL - INRIA :: [hal-00873592, version 1] DeepFlow: Large displacement optical flow with deep matching
DeepFlowではDeep Matchingという画像のマッチング手法(これも同論文内で提案されている)を用いてオプティカルフローを求めている.
以下の画像にアルゴリズムの全体像がまとめられている.
LEAR - DeepMatchingFlow
パワーポイントの資料はこちら
Deep Matching
Deep Matchingでは,畳み込み,プーリング,サブサンプリングを繰り返しすことで,画像間のQuasi-Denseなマッチング(日本語でなんていうかわからない.準密?)を求める.
Deep Learningで注目を集めているConvolutional Neural Networks(CNN)に似たアプローチとなっている.
Deep Matchingを使うと以下のように非剛体のマッチングもうまく出来るようである.
Jerome Revaud
オプティカルフロー
DeepFlowでは,時間的輝度勾配と空間的輝度勾配の拘束条件に加えて,Deep Matchingで求めた対応とオプティカルフローとの差異も拘束条件として目的関数に入れることで,密でかつ大きな動きにも強いオプティカルフローを得るようである.
目的関数の最小化はSOR法という反復法で行っているようである.
DeepFlowで以下の画像みたいな感じにマッチングとオプティカルフローが得られるらしい.
LEAR - DeepMatchingFlow
OpenCVで動かしてみる
DeepFlowは2014/8/21現在opencv_contribに入っているが,opencv_contribは開発用リポジトリらしいので自己責任で.
ここからソースを落としてきて,OpenCVのCMakeのオプションで「OPENCV_EXTRA_MODULES_PATH」を「(opencv_contribのパス)/modules」としてgenerate,ビルドしてやるとOpenCVにopencv_contrib内のモジュールが組み込まれる.
実際にDeepFlowを動かしてみたソースコードは以下.
DeepFlowのパラメータはここを参照.
#include <opencv2/opencv.hpp> #include <opencv2/optflow.hpp> void main() { using namespace cv; // DeepFlow計算用のインスタンスを生成 auto deepflow = optflow::createOptFlow_DeepFlow(); // 動画読み込み auto capture = VideoCapture("video.mp4"); // 前のフレーム保存 // まだグレースケールしか対応してないようなので変換 Mat prev, tmp; capture >> tmp; cvtColor(tmp, prev, COLOR_RGB2GRAY); while (cv::waitKey(1) == -1) { Mat curr; capture >> tmp; if (tmp.empty()) break; cvtColor(tmp, curr, COLOR_RGB2GRAY); // オプティカルフローの計算 Mat flow; deepflow->calc(prev, curr, flow); // 表示するようにX成分とY成分に分解 Mat flowXY[2]; split(flow, flowXY); // 極座標に変換 Mat magnitude, angle; cartToPolar(flowXY[0], flowXY[1], magnitude, angle, true); // 色相(H)はオプティカルフローの角度 // 彩度(S)は0~1に正規化したオプティカルフローの大きさ // 明度(V)は1 Mat hsvPlanes[3]; hsvPlanes[0] = angle; hsvPlanes[1] = magnitude; hsvPlanes[2] = Mat::ones(angle.size(), CV_32F); // HSVを合成して一枚の画像にする Mat hsv; merge(hsvPlanes, 3, hsv); // 表示 imshow("DeepFlow", hsv); // 前のフレームを保存 prev = curr; } }
結果はこんな感じ.かなり時間かかった.
ちょいちょい真っ赤になるのは仕様なのかバグなのか.
OpenCV3.0.0-alphaの特徴抽出・マッチングまとめ
OpenCV3.0.0-alphaのパッケージが公開されたと話題になっていたので使ってみる.
Change Logを見るとAKAZE特徴量が組み込まれている.
AKAZE特徴量の紹介と他特徴量との比較 - 遥かへのスピードランナー
によるとAKAZE特徴量は,以前のOpenCVでもakaze-opencvというパッケージを入れれば使えたが,OpenCV3.0.0-alphaでは元々組み込まれているのでそのまま使えるようだ.
逆にSIFTやSURFなんかは元々OpenCVで使えたが
OpenCV 3.0.0-alphaでSIFT/SURFを使う | .COM-POUND
によると,opencv_contribに移動されてしまいOpenCVと一緒にビルドしないと使えないようである.
OpenCV3.0.0-alphaでの変更点まとめ
以前当ブログで
OpenCVで画像の特徴抽出・マッチングを行う - whoopsidaisies's diary
にOpenCV2.4.7の画像の特徴抽出あたりをまとめた記事を書いていたので,今回はOpenCV3.0.0-alphaでの変更点をまとめてみる.
特徴点抽出(FeatureDetectorインターフェース)
FeatureDetector | |
---|---|
OpenCV2.4.7 | FAST,FASTX,STAR,SIFT,SURF,ORB,BRISK,MSER,GFTT,HARRIS,Dense,SimpleBlob |
OpenCV3.0.0-alpha | FAST,ORB,BRISK,MSER,GFTT,HARRIS,SimpleBlob,KAZE,AKAZE |
opencv_contrib | STAR,SIFT,SURF |
赤字はOpenCV3.0.0-alphaで追加されたもの.
青字はopencv_contribを入れれば使えるもの.
灰色はOpenCV3.0.0-alphaでなくなってしまってopencv_contribにも入ってないもの.
FASTXとDenseはなくなってしまったようである.
特徴記述(DescriptorExtractorインターフェース)
DescriptorExtractor | |
---|---|
OpenCV2.4.7 | SIFT,SURF,BRIEF,BRISK,ORB,FRIEK |
OpenCV3.0.0-alpha | BRISK,ORB,KAZE,AKAZE |
opencv_contrib | SIFT,SURF,BRIEF,FREAK |
赤字はOpenCV3.0.0-alphaで追加されたもの.
青字はopencv_contribを入れれば使えるもの.
特徴点マッチング(DescriptorMatcherインターフェース)
DescriptorExtractor | |
---|---|
OpenCV2.4.7およびOpenCV3.0.0-alpha | BruteForce,BruteForce-L1,BruteForce-SL2,BruteForce-Hamming,BruteForce-Hamming(2),FlannBased |
DescriptorMatcherに関しては変わりがないようである.
AKAZEを使ってみる
AKAZEを使ってみたかったので,OpenCV3.0.0-alphaで特徴抽出を行ってみる.ついでに他のアルゴリズムとも画像を比較してみる.特徴抽出のソースコードは以下.
cv::String detectorNames[] = {"AKAZE", "KAZE", "FAST", "ORB", "BRISK", "MSER", "GFTT", "HARRIS", "SimpleBlob",}; auto img = cv::imread("Penguins.jpg"); for (int i = 0; i < 9; i++) { auto detector = cv::FeatureDetector::create(detectorNames[i]); std::vector<cv::KeyPoint> keyPoints; detector->detect(img, keyPoints); cv::Mat output; cv::drawKeypoints(img, keyPoints, output, cv::Scalar::all(-1),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); cv::imwrite(detectorNames[i] + ".bmp", output); }
AKAZE
KAZE
FAST
ORB
BRISK
MSER
GFTT
HARRIS