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という画像のマッチング手法(これも同論文内で提案されている)を用いてオプティカルフローを求めている.

以下の画像にアルゴリズムの全体像がまとめられている.

f:id:whoopsidaisies:20140821140023j:plain

LEAR - DeepMatchingFlow

パワーポイントの資料はこちら

Deep Matching

Deep Matchingでは,畳み込み,プーリング,サブサンプリングを繰り返しすことで,画像間のQuasi-Denseなマッチング(日本語でなんていうかわからない.準密?)を求める.

Deep Learningで注目を集めているConvolutional Neural Networks(CNN)に似たアプローチとなっている.

Deep Matchingを使うと以下のように非剛体のマッチングもうまく出来るようである.

f:id:whoopsidaisies:20140821152916p:plainf:id:whoopsidaisies:20140821152927p:plain

Jerome Revaud

オプティカルフロー

DeepFlowでは,時間的輝度勾配と空間的輝度勾配の拘束条件に加えて,Deep Matchingで求めた対応とオプティカルフローとの差異も拘束条件として目的関数に入れることで,密でかつ大きな動きにも強いオプティカルフローを得るようである.

目的関数の最小化はSOR法という反復法で行っているようである.

DeepFlowで以下の画像みたいな感じにマッチングとオプティカルフローが得られるらしい.

f:id:whoopsidaisies:20140821180059p:plain

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;
	}
}

結果はこんな感じ.かなり時間かかった.
ちょいちょい真っ赤になるのは仕様なのかバグなのか.


OpenCVでDeepFlow - YouTube

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,FASTXSTARSIFTSURF,ORB,BRISK,MSER,GFTT,HARRIS,Dense,SimpleBlob
OpenCV3.0.0-alpha FAST,ORB,BRISK,MSER,GFTT,HARRIS,SimpleBlob,KAZEAKAZE
opencv_contrib STAR,SIFT,SURF

赤字はOpenCV3.0.0-alphaで追加されたもの.
青字はopencv_contribを入れれば使えるもの.
灰色はOpenCV3.0.0-alphaでなくなってしまってopencv_contribにも入ってないもの.

FASTXとDenseはなくなってしまったようである.

特徴記述(DescriptorExtractorインターフェース)

DescriptorExtractor
OpenCV2.4.7 SIFTSURFBRIEF,BRISK,ORB,FRIEK
OpenCV3.0.0-alpha BRISK,ORB,KAZEAKAZE
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

f:id:whoopsidaisies:20140820194415j:plain


KAZE

f:id:whoopsidaisies:20140820194424j:plain


FAST

f:id:whoopsidaisies:20140820194432j:plain


ORB

f:id:whoopsidaisies:20140820194437j:plain


BRISK

f:id:whoopsidaisies:20140820194444j:plain


MSER

f:id:whoopsidaisies:20140820194454j:plain


GFTT

f:id:whoopsidaisies:20140820194459j:plain


HARRIS

f:id:whoopsidaisies:20140820194510j:plain


SimpleBlog

f:id:whoopsidaisies:20140820194515j:plain

C#でDeep Learning(Accord.NETによる方法)

流行りのDeep LearningをC#で試してみる.機械学習やコンピュータビジョン,信号処理等の.NET向けのオープンソースのライブラリであるAccord.NET FrameworkにDeep Learningが実装されているのでそれを使う.

Deep Belief Networks(DBN), Deep Neural Networks(DNNs)とおまけにRestricted Boltzmann Machine(RBM)を単体で動かしてみる.

Deep Learning自体については以下のページ等を参考に.
MIRU2014 tutorial deeplearning
Deep Learning技術の今
ディープラーニング チュートリアル(もしくは研究動向報告)

Accord.NETのインストール

NuGet経由でインストール可能.プロジェクトを右クリックして「NuGetパッケージの管理」を選択する.オンラインから「Accord.Neuro」を検索・インストール.

f:id:whoopsidaisies:20140819013518p:plain

ソースコード

を動かすサンプルコードを載せていくが,いずれのサンプルでも以下のusingを書いておくこと.

using Accord.Neuro;
using Accord.Neuro.Networks;
using Accord.Neuro.Learning;
using AForge.Neuro.Learning;
using Accord.Neuro.ActivationFunctions;
using Accord.Math;

Deep Belief Networks(DBN) - 教師あり学習(Back propagation)

// トレーニングデータ
double[][] inputs = {
    new double[] { 1, 1, 1, 0, 0, 0 },
    new double[] { 1, 0, 1, 0, 0, 0 },
    new double[] { 1, 1, 1, 0, 0, 0 },
    new double[] { 0, 0, 1, 1, 1, 0 },
    new double[] { 0, 0, 1, 1, 0, 0 },
    new double[] { 0, 0, 1, 1, 1, 0 },
};
double[][] outputs = {
    new double[] { 1, 0 },
    new double[] { 1, 0 },
    new double[] { 1, 0 },
    new double[] { 0, 1 },
    new double[] { 0, 1 },
    new double[] { 0, 1 },
};

// DBNの生成
var network = new DeepBeliefNetwork(
    inputsCount: inputs.Length,         // 入力層の次元
    hiddenNeurons: new int[] { 4, 2 }); // 隠れ層と出力層の次元

// ネットワークの重みをガウス分布で初期化する
new GaussianWeights(network).Randomize();
network.UpdateVisibleWeights();

// DBNの学習アルゴリズムの生成  5000回繰り返し入力
var teacher = new BackPropagationLearning(network);                        
for (int i = 0; i < 5000; i++)
    teacher.RunEpoch(inputs, outputs);

// 重みの更新
network.UpdateVisibleWeights();

// 学習されたネットワークでテストデータが各クラスに属する確率を計算
double[] input = { 1, 1, 1, 1, 0, 0 };
var output = network.Compute(input);

//  一番確率の高いクラスのインデックスを得る
int imax; output.Max(out imax);

// 結果出力
Console.WriteLine("class : {0}", imax);
foreach (var o in output)
{
    Console.Write("{0} ", o);
}

実行結果

class : 0
0.883118949465337 0.115560850111682

Deep Belief Networks(DBN) - 教師なし学習(Contrastive Divergence)

// トレーニングデータ
double[][] inputs = {
    new double[] { 1, 1, 1, 0, 0, 0 },
    new double[] { 1, 0, 1, 0, 0, 0 },
    new double[] { 1, 1, 1, 0, 0, 0 },
    new double[] { 0, 0, 1, 1, 1, 0 },
    new double[] { 0, 0, 1, 1, 0, 0 },
    new double[] { 0, 0, 1, 1, 1, 0 },
};

// DBNの生成
var network = new DeepBeliefNetwork(
    inputsCount: 6,
    hiddenNeurons: new int[] { 4, 2 });

// ネットワークの重みをガウス分布で初期化する
new GaussianWeights(network).Randomize();
network.UpdateVisibleWeights();

// DBNの学習アルゴリズムの生成  5000回繰り返し入力
var teacher = new DeepBeliefNetworkLearning(network)
{
    Algorithm = (h, v, i) => new ContrastiveDivergenceLearning(h, v)
};
var layerData = teacher.GetLayerInput(inputs);
for (int i = 0; i < 5000; i++)
    teacher.RunEpoch(layerData);
// 重みの更新
network.UpdateVisibleWeights();

// 学習されたネットワークでテストデータが各クラスに属する確率を計算
double[] input = { 1, 1, 1, 1, 0, 0 };
var output = network.Compute(input);

//  一番確率の高いクラスのインデックスを得る
int imax; output.Max(out imax);

// 結果出力
Console.WriteLine("class : {0}", imax);
foreach (var o in output)
{
    Console.Write("{0} ", o);
}

実行結果

class : 1
0.529437183967883 0.554114047128916

Deep Neural Networks(DNNs)

// トレーニングデータ
double[][] inputs = {
    new double[] { 1, 1, 1, 0, 0, 0 },
    new double[] { 1, 0, 1, 0, 0, 0 },
    new double[] { 1, 1, 1, 0, 0, 0 },
    new double[] { 0, 0, 1, 1, 1, 0 },
    new double[] { 0, 0, 1, 1, 0, 0 },
    new double[] { 0, 0, 1, 1, 1, 0 },
};
double[][] outputs = {
    new double[] { 1, 0 },
    new double[] { 1, 0 },
    new double[] { 1, 0 },
    new double[] { 0, 1 },
    new double[] { 0, 1 },
    new double[] { 0, 1 },
};

// ネットワークの生成
var network = new DeepBeliefNetwork(
    inputsCount: inputs.Length,         // 入力層の次元
    hiddenNeurons: new int[] { 4, 2 }); // 隠れ層と出力層の次元

// DNNの学習アルゴリズムの生成
var teacher = new DeepNeuralNetworkLearning(network)
{
    Algorithm = (ann, i) => new ParallelResilientBackpropagationLearning(ann),
    LayerIndex = network.Machines.Count - 1,
};

// 5000回繰り返し学習
var layerData = teacher.GetLayerInput(inputs);
for (int i = 0; i < 5000; i++)
    teacher.RunEpoch(layerData, outputs);

// 重みの更新
network.UpdateVisibleWeights();

// 学習されたネットワークでテストデータが各クラスに属する確率を計算
double[] input = { 1, 1, 1, 1, 0, 0 };
var output = network.Compute(input);

//  一番確率の高いクラスのインデックスを得る
int imax; output.Max(out imax);

// 結果出力
Console.WriteLine("class : {0}", imax);
foreach (var o in output)
{
    Console.Write("{0} ", o);
}

実行結果

class : 0
0.999680477117216 0.00120961077917418

Restricted Boltzmann Machine(RBM)

// トレーニングデータ
double[][] inputs = {
    new double[] { 1, 1, 1, 0, 0, 0 },
    new double[] { 1, 0, 1, 0, 0, 0 },
    new double[] { 1, 1, 1, 0, 0, 0 },
    new double[] { 0, 0, 1, 1, 1, 0 },
    new double[] { 0, 0, 1, 1, 0, 0 },
    new double[] { 0, 0, 1, 1, 1, 0 },
};
double[][] outputs = {
    new double[] { 1, 0 },
    new double[] { 1, 0 },
    new double[] { 1, 0 },
    new double[] { 0, 1 },
    new double[] { 0, 1 },
    new double[] { 0, 1 },
};

// RBMの生成
var rbm = new RestrictedBoltzmannMachine(
    inputsCount: 6, 
    hiddenNeurons: 2);

// トレーニングデータで学習
var teacher = new ContrastiveDivergenceLearning(rbm);
for (int i = 0; i < 5000; i++)
    teacher.RunEpoch(inputs);

// テストデータ
double[] input = { 1, 1, 1, 1, 0, 0 };

// 学習されたネットワークで各クラスに属する確率を計算
var output = rbm.Compute(input);

//  一番確率の高いクラスのインデックスを得る
int imax; output.Max(out imax);

// 結果出力
Console.WriteLine("class : {0}", imax);
foreach (var o in output)
{
    Console.Write("{0} ", o);
}

実行結果

class : 1
0.133832413712274 0.906343089146992

C#でSVM libsvm.netによる方法

SVMのライブラリであるLIBSVMを.NETプロジェクトで使えるようにするlibsvm.netを動かしてみる.

libsvm.netのインストール

NuGet経由でインストール可能.プロジェクトを右クリックして「NuGetパッケージの管理」を選択する.オンラインから「libsvm.net」を検索・インストール.

動作確認

libsvm.netでは,トレーニングデータやテストデータをLIBSVMと同じ形式でファイルから入力できる.データのフォーマットは以下の通りである.

<label> <index1>:<value1> <index2>:<value2> ... <indexN>:<valueN>
<label> <index1>:<value1> <index2>:<value2> ... <indexN>:<valueN>
<label> <index1>:<value1> <index2>:<value2> ... <indexN>:<valueN>
.
.
.
<label> <index1>:<value1> <index2>:<value2> ... <indexN>:<valueN>

LIBSVMの入力データ形式について

ここにLIBSVM用のデータセットが落ちていたので,fourclassというデータセットを使ってみる.

以下に,分類器を動かすソースコード乗せる.

// トレーニングデータの読み込み
var training = libsvm.ProblemHelper.ReadProblem("fourclass");
// RBFカーネルを生成
var gamma = 1;
var kernel = libsvm.KernelHelper.RadialBasisFunctionKernel(gamma);
// C-SVC用のクラスの生成
var c = 1;
var svm = new libsvm.C_SVC(training, kernel, c);

// テストデータの読み込み(面倒だったのでトレーニングデータをそのまま使ってる)
var test = libsvm.ProblemHelper.ReadProblem("fourclass");
for (int i = 0; i < test.l; i++)
{
    // 予測値
    var z = svm.Predict(test.x[i]);
    // 入力ファイルについているラベル
    var y = test.y[i];
    // 予測値と正解値を出力
    Console.WriteLine("{0}:{1} {2}", i, z, y);
}

トレーニングデータとテストデータは同じものを使った.実行すると,予測値と正解値が出力される.

上記コードではパラメータ選択とかまでは行っていないが,結構簡単に使えそう.他にも回帰 (ϵ-SVR)や外れ値検出 (One-Class SVM) が出来るようである.

C# LINQでFIRフィルタ

LINQを使った,FIRフィルタ演算を行う関数.

ソースコード

/// <summary>
/// FIRフィルタをかけたデータを返す
/// </summary>
/// <param name="data">入力データ</param>
/// <param name="coefficients">フィルタ係数</param>
/// <returns>FIRフィルタをかけたデータ</returns>
List<double> FiniteImpulseResponseFilter(
    List<double> data,
    List<double> coefficients)
{
    return Enumerable.Range(0, data.Count - coefficients.Count)
        .Select(i => data.Skip(i).Take(coefficients.Count)
            .Zip(coefficients, (d, c) => d * c).Sum())
        .ToList();
}

C# LINQで移動平均 - whoopsidaisies's diary
をベースにした.

.Zip(coefficients, (d, c) => d * c).Sum()

の部分で入力データとフィルタ係数の積和演算を行っている.

C# LINQで移動平均

LINQを使った,移動平均を計算する関数.

ソースコード

/// <summary>
/// 移動平均を格納した配列を返す
/// </summary>
/// <param name="data">入力データ</param>
/// <param name="num">何個分の平均をとるか</param>
/// <returns>移動平均をとったデータ</returns>
List<double> MovingAverage(
    List<double> data,
    int num)
{
    return Enumerable.Range(0, data.Count - num)
        .Select(i => data.Skip(i).Take(num).Average())
        .ToList();
}

LINQの練習用なので効率が良いかとかは知らない.

Enumerable.Range(0, data.Count - num)

で,0~総データ数-平均を求めるときのデータの個数までの整数のシーケンスを生成する.

data.Skip(i).Take(num).Average()

で,入力データのi番目のデータからnum個データを抽出して平均をとる.

その他

FIRフィルタは以下の記事.
C# LINQでFIRフィルタ - whoopsidaisies's diary

C#でExcelファイルを作成・グラフを挿入する NetOfficeによる方法

C#からExcelファイルを作って,グラフも挿入したい.

以前,以下の記事でClosedXmlというライブラリを使ってC#から簡単にExcelファイルを作る方法を紹介したが,グラフの挿入が出来ないのがグラフの挿入が出来ないのが気になっていた.
C#でExcelファイルを作成する ClosedXmlを使用 - whoopsidaisies's diary

NetOfficeというライブラリを使えば,ClosedXmlと同じくらい簡単にExcelファイルが作成でき,さらにグラフの挿入も行えるので,今回はその方法を紹介する.

NetOfficeのインストール

NetOfficeはMicrosoft Officeにアクセスするための.NETのライブラリで,今回紹介するExcel以外にも,Word, PowerPoint等にもアクセスできるらしい.

インストールはNuget経由で可能.プロジェクトを右クリックして「NuGetパッケージの管理」を選択する.オンラインから検索て「NetOffice.Excel」をインストールする.

f:id:whoopsidaisies:20140619095605p:plain

動作確認

以下,動作確認用のソースコードと生成されたExcelファイルの中身.実行環境ではExcel2010がインストール済み.

class Program
{
    static void Main(string[] args)
    {
        // Excelを開く
        using (var excelApplication = new NetOffice.ExcelApi.Application())
        {
            // ワークブックを追加
            var workBook = excelApplication.Workbooks.Add();
            // ワークシートを取得
            var workSheet = (NetOffice.ExcelApi.Worksheet)workBook.Worksheets[1];

            // データの入力
            workSheet.Cells[1, 2].Value = "ブラウザシェア";
            workSheet.Cells[2, 1].Value = "IE";
            workSheet.Cells[2, 2].Value = "52.56%";
            workSheet.Cells[3, 1].Value = "Chrome";
            workSheet.Cells[3, 2].Value = "24.7%";
            workSheet.Cells[4, 1].Value = "Firefox";
            workSheet.Cells[4, 2].Value = "12.92%";
            workSheet.Cells[5, 1].Value = "その他";
            workSheet.Cells[5, 2].Value = "=1-B2-B3-B4";
                
            // グラフオブジェクトの追加
            var chart = ((NetOffice.ExcelApi.ChartObjects)workSheet.ChartObjects()).Add(70, 100, 375, 225);
            // 円グラフに設定
            chart.Chart.ChartType = NetOffice.ExcelApi.Enums.XlChartType.xlPie;
            // データ範囲を指定
            chart.Chart.SetSourceData(workSheet.Range("A1:B5"));

            // 保存(環境に合わせて拡張子xlsとかでも大丈夫みたい)
            workBook.SaveAs(@"d:\test.xlsx");

            // Excelを終了する
            excelApplication.Quit();
        }
    }
}

出来上がったExcelファイルの中身はこんな感じ.

f:id:whoopsidaisies:20140619094620p:plain

その他

以下のページにグラフ以外のサンプルも多数ある.
NetOffice - MS Office in .NET - Home
【NetOffice】【Excel】NetOfficeのまとめ | 創造的プログラミングと粘土細工(こっちは日本語)