2017年7月10日月曜日

OpticalFlowをしてみた

動画の中で物がどれだけ動いているかを数値化するOpticalFlowというものがあります。
これを使うと、人や物がどんだけ動いているか画像からわかるらしいです。
これを使って自動運転車とかが歩行者検出とかしているのね。

ということで、よしいつものようにC++でOpticalFlowの実験。
まず、いつものように、OpticalFlowを実装したC++のライブラリを探すのですが、どうもちゃんと使えるOpticalFlowはまだOpenCVでしか実装されていないようです。
OpenCVでかいから使いたくないんだよね。

なので、今回はOpenCVからOpticalFlowの関連部分だけを抜き出して使うことに挑戦します。OpenCVって全ビルドすると何百メガもあるんですが、必要なところだけを抜き出して使うと数メガバイトで済むのです。


まず、OpenCV2.4.13.2をとってきて、core部分のビルドをします。
coreというのはMatクラスの部分です。ベクトル演算をするところです。

modules/core/srcとmodules/core/includeにある*.cppと*.hのファイルをどこかのディレクトリに入れます。
次に、*.hと*.cppファイルからincludeしているファイルのパスの部分だけを消します。


#include "opencv2/core/types_c.h"


#include "types_c.h"


こんな感じでcoreの部分のファイルを全部持ってきてコンパイルすると、Matクラスが使えるようになります。

これでOpenCVのベクトル演算部分だけ完成。

これと同じことをimgprocにもすると、OpenCVの画像処理変換処理とベクトル演算部分だけを使うことができます。

世の中の大部分の人は、OpenCVの画像処理とベクトル演算部分しか使ってないと思うんですよね。coreとimgprocの50個くらいファイルを取ってきてコンパイルすれば、コンパクトなOpenCVを作ることができます。

画像の読み書きの部分は入っていませんが、この二つのモジュールだけもかなりの処理ができ、画像の変換、リサイズ、をすることだってできます。

最後にOpticalFlowやTemplateMachingの部分のソースのoptiflowgf.cppやtemplmatch.cppを持ってきます。

これでOpticalFlowをするための最小のOpenCVが完成。


--------------------------

#include <stdio.h>
#include <vector>

#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION

#include "core.hpp"
#include "imgproc.hpp"
#include "tracking.hpp"

#include "stb_image.h"
#include "stb_image_write.h"

#define X_DIV 10
#define Y_DIV 10



using namespace std;


void changeb_g(unsigned char* p,int x,int y,int c)
{
int ct = x*y;
int i;
unsigned char t;
for (i = 0; i < ct; i++){
t = p[0];
p[0] = p[2];
p[2] = t;
p[3] = 255;
p += c;
}
}

int main()
{
printf("Hello Open CV!");
unsigned char*p, *q;
int x=-1, y=-1;
int n;
int i, j;

//load image
p = stbi_load("image00001.jpg", &x, &y, &n, 4);
q = stbi_load("image00004.jpg", &x, &y, &n, 4);
if (p == NULL || q == NULL || x < 1 || y < 1)return 1;

// R<-->B
changeb_g(p, x, y, 4);
changeb_g(q, x, y, 4);

cv::Mat color = cv::Mat(y, x, CV_8UC4);
cv::Mat color2 = cv::Mat(y, x, CV_8UC4);
cv::Mat gray;
cv::Mat gray2;
cv::Mat flow;
cv::Mat plane[2];
cv::Mat mag,ang;
float a;

//copy data
memcpy(color.data, p, x * 4 * y);
memcpy(color2.data, q, x * 4 * y);
stbi_image_free(p);
stbi_image_free(q);

// conver to gray scale
cv::cvtColor(color, gray, CV_BGR2GRAY);
cv::cvtColor(color2, gray2, CV_BGR2GRAY);

// calc optical flow
cv::calcOpticalFlowFarneback(gray, gray2, flow, 0.5, 3, 15, 3, 5, 1.2, 0);
cv::split(flow, plane);
cv::cartToPolar(plane[0], plane[1], mag,ang);

char kekka[Y_DIV][X_DIV];
int x_step = x / X_DIV;
int y_step = y / Y_DIV;

for (i = 0; i < Y_DIV; i++)for (j = 0; j < X_DIV; j++){
if (j == 0)printf("\n");
ang = mag(cv::Rect(x_step*j, y_step*i, x_step, y_step));
a = cv::mean(ang)[0];
a = log10(a) * 20;
kekka[i][j] = 0;
if (a>0)kekka[i][j]=1;
}

for (i = 0; i < Y_DIV; i++){
for (j = 0; j <X_DIV; j++){
printf("%d  ",kekka[i][j]);
}
printf("\n");
}

return 0;
}

--------------------------
この二つの画像から動いている部分を検出します。



画像の動いている部分が1になるようにしてみました。


0  0  0  0  0  0  0  0  1  1
0  0  0  0  0  0  0  0  1  1
0  0  0  0  0  0  0  0  0  0
0  0  0  0  0  0  0  0  0  0
0  0  0  0  0  0  0  0  0  0
0  0  0  0  0  0  0  0  0  0
0  0  0  0  0  0  0  0  0  0
0  0  0  0  0  0  0  0  0  0
0  0  0  0  0  0  0  0  0  0
0  0  0  0  0  0  0  0  0  0


どうやら手の指の部分が動いてるらしい

0 件のコメント:

コメントを投稿