2017年10月24日火曜日

QRコードをデコードする

世の中にはあらゆるソースコードがあふれているんですが、ありえないことにQRコードをデコードできるフリーのライブラリっていまだにあんまりないのね。
libdecoderqrというライブラリが一番有名なんですが、OpenCVガンガンに使っているからでかいし使いづらい。
これではラズパイとかで使えないじゃないか。

というわけで、以前作った最小のOpenCVを元に、外部のライブラリに依存しない単独で動くlibdecoderqrを作ってみました。
ライブラリのソースもかなり古いので、最新のg++やclangでもコンパイルできるようにして完成。



ソースはこちら。
たまにはGitHubにでもコミットして世の中に貢献しなくては。
https://github.com/yomei-o/libdecoderqr



コンソールから
g++ *.cpp
でコンパイルできます。

MacOSXのg++とVisualStudio2013でコンパイルできたので、多分Linuxでも動くと思います。

サイズが小さいので組み込みやスマホアプリの開発にも使えると思います。
これで以前このブログで紹介したlibwebcamと組み合わせて、USBカメラとラズパイでQRコードを使ったプログラムを動かすことができます。


USBカメラでとった画像をいろいろごにゅごにょするのって意外と面白いですね。
オープンソースだけで短期間にいろいろな面白いシステムを組むことができます。

2017年8月22日火曜日

視差マップを作る

おじさんの画像処理勉強シリーズも毎日こつこつひとつづつやってると、勉強することがだんだんなくなってきますね。

今日はアイサイトで使われている。視差マップに挑戦です。
二つのカメラで撮影した画像から物体の距離を求めることができます。

ほんとうはLiDARとかのレーダーレーダーを使って障害物を検出したいのですが、個人的にやるのは無理なので、簡単な複数のカメラを使った障害物検出にしました。

実際にアイサイトは特徴点にたいする視差マップを使っていますが、視差マップ自体は
特徴点がなくても作ることができます。

ということでいつものように勉強
http://ishidate.my.coocan.jp/opencv_20/opencv_20.htm

なるほど。StereoSGBM(ステレオセミグローバルブロックマッチング)ということをすればよいのね。

いつものようにコピペ流でコードを書きます。
といってもコアの処理の部分はたったの四行なので誰でも作れます。
オリジナルはRGBで処理していますが、移植をしやすいようにRGBAの32ビットで処理するように変更しました。

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

#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION

#include "core.hpp"
#include "imgproc.hpp"
#include "tracking.hpp"
#include "calib3d.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;
}
}

void changeToHueColor(cv::Mat src,cv::Mat& dst)
{
cv::Mat channels[3];
channels[0] = cv::Mat(src.size(), CV_8UC1);
channels[1] = cv::Mat(src.size(), CV_8UC1,255);
channels[2] = cv::Mat(src.size(), CV_8UC1,255);
cv::Mat hsv_image,tmp;
int d,i,j;
for (i = 0; i < src.cols;i++)
for (j = 0; j < src.rows; j++)
{
d = src.at<uchar>(j, i);
channels[0].at<uchar>(j, i) = (255 - d) / 2;
}
cv::merge(channels, 3, hsv_image);
cv::cvtColor(hsv_image, tmp, CV_HSV2BGR);
cv::cvtColor(tmp, dst, CV_BGR2BGRA);
}


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

//load image
//p = stbi_load("dave.jpg", &x, &y, &n, 4);
p = stbi_load("Tsukuba_L.png", &x, &y, &n, 4);
q = stbi_load("Tsukuba_R.png", &xx, &yy, &nn, 4);
if (p == NULL || q == NULL || x < 1 || y < 1 || x != xx || y != yy){
if (p)stbi_image_free(p);
if (q)stbi_image_free(q);
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,gray2;

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

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

cv::Mat disparity_data, disparity_map;
double min_, max_;

//cv::StereoBM sbm = cv::StereoBM(0, 16, 15);
//sbm.state->speckleWindowSize = 50;
//sbm.state->speckleRange = 1;
//sbm.operator()(gray, gray2, disparity_data);

cv::StereoSGBM ssgbm = cv::StereoSGBM(0, 16, 15);
ssgbm.speckleWindowSize = 200;
ssgbm.speckleRange = 1;
ssgbm.operator()(gray, gray2, disparity_data);


cv::minMaxLoc(disparity_data, &min_, &max_);
disparity_data.convertTo(disparity_map, CV_8UC1, 255 / (max_ - min_), -255 * min_ / (max_ - min_));
//cv::equalizeHist(disparity_map, disparity_map);
changeToHueColor(disparity_map, disparity_map);
changeb_g(disparity_map.data, x, y, 4);
stbi_write_png("result.png", x, y, 4, disparity_map.data, 4 * x);

return 0;
}
-----------------------------

というわけでこのふたつの画像の視差マップを作ってみました。



equalizeHist関数はGPUがないと動かないので、使わないように変更。
近い部分が赤色になるようです。



なんか正しくできたっぽい。
障害物検出も簡単。




2017年8月21日月曜日

道路の白線認識をする。

最近、自動運転って検索するとおじさんのブログが出てきます。
おじさん、自動運転の仕事なんてやってないんだけどなぁ・・・。

おじさん、お盆休みにレンタカーを借りました。
最新のレンタカーってすごいんです。車が道路の白い線からはみ出ると、ぴーっと音が鳴るんです。
最近の車は白線がどこがわかるんですね。すげー。

おじさん、これ作ってみたい。
というわけで、本日は白線認識というものをやってみます。

どうやるか調べてみると、なんかHough(ハフ)変換というものを使うと、直線とか曲線とかがわかるらしい。
https://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%95%E5%A4%89%E6%8F%9B

OpenCVにも関数あるし。
http://opencv.jp/opencv-2svn/cpp/feature_detection.html


というわけで以前のブログで作った最小のOpenCVでHough(ハフ)変換のコードを作成。
その辺のWebに落ちている写真で白線認識してみました。


--------------------------------------
#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!\n");
unsigned char*p;
int x=-1, y=-1;
int n,m;
int i;

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

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

cv::Mat color = cv::Mat(y, x, CV_8UC4);
cv::Mat gray,edges;
vector<cv::Vec4i> lines;

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

// conver to gray scale 
cv::cvtColor(color, gray, CV_BGR2GRAY);
cv::Canny(gray, edges, 50, 150, 3);

// calc hough
//cv::HoughLinesP(edges, lines, 1, 3.14159265 / 180, 100, 100, 10);
cv::HoughLinesP(edges, lines, 1, 3.14159265 / 180, 100, 100, 3);
n = lines.size();
//printf("lines.size=%d\n", n);
for (i = 0; i < n; i++){
//printf("%d  %d  %d  %d\n", lines[i][0], lines[i][1], lines[i][2], lines[i][3]);
cv::line(color, cv::Point(lines[i][0], lines[i][1]), cv::Point(lines[i][2], lines[i][3]), cv::Scalar(0, 0, 255), 2);
}

changeb_g(color.data, x, y, 4);
stbi_write_png("result.png", x, y, 4, color.data, 4 *x);
return 0;
}
--------------------------------------

ほんとうに数行コードを書くだけでできちゃうんですね。
自動運転って意外と簡単?



この画像から白線を検出すると



このようになるらしいです。

前回ブログで紹介した射影変換と組み合わせれば道路内で自分の走っている位置とかもわかっちゃいます。


ということで、本日は白線認識についてでした。








2017年7月31日月曜日

射影変換してみた

スマホのカメラなどでとった写真で斜めになっているものを、正面からみたように変換する射影変換というものがあります。
日産の車の360度アラウンドビューモニターとかで使われているあれですね。

個人でアラウンドビューモニター作ってやろうじゃないの。
ということでいつものように、C++で射影変換書いてみました。

http://wildpie.hatenablog.com/entry/20141112/1415802846
理論とかはこのページに書いてあります。

ベクトルって難しいなぁ。
理論をよく読んでC++で実装します。
といっても関数は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 xx 300
#define yy 300

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!\n");
unsigned char*p;
int x=-1, y=-1;
int n;
int i, j;
//load image
p = stbi_load("test.png", &x, &y, &n, 4);
if (p == NULL || x < 1 || y < 1)return 1;

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

cv::Mat img = cv::Mat(y, x, CV_8UC4);

cv::Mat res;

//copy data
memcpy(img.data, p, x * 4 * y);
stbi_image_free(p);

cv::Size sz = cv::Size(xx, yy);

cv::Point2f pts1[] = { { 175, 122 }, { 265, 125 }, { 179, 255 }, { 269, 238 }};
cv::Point2f pts2[] = { { 0, 0 }, { xx, 0 }, { 0, yy }, { xx, yy } };

cv::Mat M = cv::getPerspectiveTransform(pts1, pts2);
cv::warpPerspective(img, res,M, sz);

// R<-->B
changeb_g(res.data, xx, yy, 4);
stbi_write_png("result.png", xx, yy, 4, res.data, 4 * xx);


return 0;
}

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



たしかに正しくできた感じ。

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


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

2017年7月6日木曜日

libwebcamとUSBカメラで遊ぶ

最近のディープラーニングブームで、USBカメラを使って画像処理をする機会が増えています。みんなどうやってUSBカメラから画像を取ってきているのかと調べてみると、多くの人がOpenCVやffmpegで動画を取ってきているようです。

OpenCVでかいし、コンパイルめんどくさくない?
バージョンどんどん上がって互換性なくてコンパイルが通らないし。

ということで、今回は手軽にUSBカメラで画像をキャプチャーすることができるライブラリlibwebcamを使って画像処理をしてみたいと思います。

このlibwebcamすごいです。WindowsでもLinuxでも同じようにUSBカメラから画像をキャプチャーできるのです。WindowsはDirectShow経由で、LinuxはV4L2経由で画像を取ってくるので、RasberyPiや試してませんがやAndroid等でも動くはずです。

いつものようにlibwebcamのサイトからソースコードをとってきます。
http://rojarand.github.io/libwebcam/


Linuxのほうは簡単にビルドができるのですが、いつものおじさんのWindowsしかもVirtualBox上のVisualStudio12ではビルドができない。
というかファイル足りないし。

ソースを見ながらどうすればVisualStudio2012や最新のVisualStudio2017でビルドできるか考えます。

Windows版をビルドするには、どうやらWindows7SDKにはいっているDirectShowのSampleが必要なようです。なのでWindows7SDKにあるDirectShowのサンプルのbaseclassesのソースコードを一式を持ってきます。これWindows8SDKにはないってないんです。どうしてもWindows7SDKを入れないといけない。

このソースも一緒にコンパイルすると、次はCLSID_hogehogeが解決しませんとかいうたくさんのリンクエラー。うーんこれはどうやって解決するか書くとめんどくさいので、とりあえず以下のファイルを作ってみました。
このファイルも一緒にコンパイルするとリンクエラーがなくなります。

--------------------
#ifdef WIN32
#pragma comment(lib,"winmm.lib")
#pragma comment(lib,"strmbase.lib")
#endif

#ifdef _WIN32

#define INITGUID

#include <objbase.h>
#include <uuids.h>

#endif
--------------------


これで、ビルドができ、ビットマップファイルを作るサンプルが動きます。
でもここでまた問題が、このサンプルはLinuxでは動きませんと書いてある・・・。
なんでだよ。

いろいろ調べてみるとどうやら、USBカメラというものはUSBのバス上をMotionJPEGというJPEGファイルでデータが来るらしい。さらにWindowsはDirectShowがフォーマット変換をするのでRGB形式など違うフォーマットで各フレームのデータが受け取れるけれど、LinuxのV4L2はフォーマット変換をしないのでJPEGでしかフレームデータを受け取れないみたいです。


ということで、サンプルを以下のようにJPEGファイルを書き出すサンプルに書き直してみました。

--------------------
#include <iostream>
#include <sstream>
#include <fstream>
#include <chrono>
#include <iomanip>
#include <cstring>
#include <libwebcam/webcam.h>

webcam::buffer create_bitmap_buffer(webcam::image * image_)
{
return webcam::buffer((const unsigned char*)image_->get_data(), image_->get_data_lenght());
}

void save_bitmap_to_file(const webcam::buffer & buffer_, unsigned int image_index_)
{
std::stringstream file_name_stream;
file_name_stream << "image" << std::setfill('0') << std::setw(5) << image_index_ << ".jpg";
std::string file_name = file_name_stream.str();

std::ofstream image_file_stream;
image_file_stream.open(file_name.c_str(), std::ios::out | std::ios::binary);
image_file_stream.write((const char*)buffer_.get_data(), buffer_.get_length());
image_file_stream.close();
}

void write_images(const int image_count_, webcam::device & device_){

for (int image_index = 0; image_index < image_count_; ){

webcam::image * image = device_.read();
if (!image->is_empty()){
printf("buffer_length()=%d  \n", image->get_data_lenght());
webcam::buffer buffer = create_bitmap_buffer(image);
std::cout << "writing bitmap no: " << image_index << std::endl;
save_bitmap_to_file(buffer, image_index++);
}
delete image;
}
}

int main(int argc, char **argv)
{
try
{
const webcam::device_info_enumeration & enumeration = webcam::enumerator::enumerate();
const size_t count = enumeration.count();
if (count == 0){

std::cout << "There is no webcam available on this computer" << std::endl;
return 0;
}

const webcam::device_info & device_info = enumeration.get(0);
const webcam::video_info_enumeration & video_info_enumeration = device_info.get_video_info_enumeration();
const webcam::video_info & video_info = video_info_enumeration.get(0);

const unsigned char device_number = 1;

webcam::video_settings video_settings;
//force bitmap as output format - it may throw exception under linux
video_settings.set_format(webcam::format_MJPEG());

//video_settings.set_resolution(video_info.get_resolution());
webcam::resolution res(640, 480);
video_settings.set_resolution(res);

video_settings.set_fps(30);

webcam::device device(device_number, video_settings);
std::cout << "Trying to open device with number: " << (int)device_number << ", fps:" << (int)video_settings.get_fps() << std::endl;
device.open();
write_images(10, device);
device.close();
}
catch (const webcam::webcam_exception & exc_)
{
std::cout << "error occured: " << exc_.what() << std::endl;
}
return 0;
}
--------------------

わかりづらいなぁ、はじめからこのサンプルにしとけよ。
でもこれで、WindsowsでもLinuxでもラズパイでもAndroidでもカメラからキャプチャーをしてJPEG形式ですがフレームデータを受け取ることができます。


最後にこのJPEGをどうやってRGB形式に変換するのか、また、このたくさんのJPEGのフレームデータからどうやって動画ファイルを作るのか。
これは以前このブログで紹介したSTBという画像処理ライブラリ、とJO_MPEGというMPEG書き出しライブラリで行います。

STB
https://github.com/nothings/stb


JO_MPEG
http://www.jonolick.com/home/mpeg-video-writer

こんな感じに書けばUSBカメラの画像をMPEGファイルに書き出せます。
-------------------
#define STB_IMAGE_IMPLEMENTATION

#include <iostream>
#include <sstream>
#include <fstream>
#include <chrono>
#include <iomanip>
#include <cstring>
#include <libwebcam/webcam.h>

#include "jo_mpeg.h"
#include "stb_image.h"


void write_images(const int image_count_, webcam::device & device_){

FILE* fp;
unsigned char* buf = NULL;
int w = 640, h = 480;
int ww,hh, comp, sz;
fp = fopen("test.mpg", "wb");
if (fp == NULL){
return;
}
for (int image_index = 0; image_index < image_count_; image_index++){

webcam::image * image = device_.read();
if (!image->is_empty()){
buf=stbi_load_from_memory(image->get_data(),image->get_data_lenght(), &ww, &hh, &comp, 4);
jo_write_mpeg(fp, (const unsigned char*)buf, w,h, 30);  // frame 0
stbi_image_free(buf);
}
delete image;
}
//delete[] buf;
fclose(fp);
}

int main(int argc, char **argv)
{
try
{
const webcam::device_info_enumeration & enumeration = webcam::enumerator::enumerate();
const size_t count = enumeration.count();
if (count == 0){

std::cout << "There is no webcam available on this computer" << std::endl;
return 0;
}

const webcam::device_info & device_info = enumeration.get(0);
const webcam::video_info_enumeration & video_info_enumeration = device_info.get_video_info_enumeration();
const webcam::video_info & video_info = video_info_enumeration.get(0);

const unsigned char device_number = 1;

webcam::video_settings video_settings;
//force bitmap as output format - it may throw exception under linux
video_settings.set_format(webcam::format_MJPEG());

//video_settings.set_resolution(video_info.get_resolution());
webcam::resolution res(640, 480);
video_settings.set_resolution(res);

video_settings.set_fps(30);

webcam::device device(device_number, video_settings);
std::cout << "Trying to open device with number: " << (int)device_number << ", fps:" << (int)video_settings.get_fps() << std::endl;
device.open();
write_images(300, device);
device.close();
}
catch (const webcam::webcam_exception & exc_)
{
std::cout << "error occured: " << exc_.what() << std::endl;
}
return 0;
}
-------------------


これで、ラズパイでOpenCVを使わず顔認識とかするロボットが作れる。
ひょっとすると車の自動運転も夢じゃないな。

今日はOpenCVを使わないで手軽にUSBカメラで画像認識をする方法でした。





2017年6月28日水曜日

強化学習してみた。

昨日書いた、便利なtiny-dnnとstbライブラリ。さらにstbの動画版の簡単に動画ファイルを出力できるjo_mpeg。
これらのライブラリと強化学習と組み合わせれば、CaffeとかKerasとかを使わないでDQN(深層Qネットワーク)とかできんでない?

と思ったのですが、おじさん仕事が忙しくて、実装や学習させる根性がないので、今日はまずC++で普通の強化学習をやるところまで。

強化学習ってなんなのかというと、ここに理論とソースコードが書いてあります。
http://kivantium.hateblo.jp/entry/2015/09/29/181954

すごいねー。おじさんが大学生だった時代はこんな便利なWebページなかったのね。
深くない普通の強化学習はとってもわかりやすいですね。
おじさんのような浅い知識でも大丈夫。

いつものようにここのソースをそのままとってきて、ちょっと改良。
stbで途中経過と結果の画像を作ります。

おじさん意地でもC++でやるんです。
なんか同じ結果になったぞ。




これに深層学習とかDNNとかを組み合わせるとDQNになるらしい。
ほんとかよ。
ということで、次はC++でtiny-DQNとかやりたいんだけどなー。
そんなに暇ないしなー。誰かライブラリ作ってくれないかなぁ。



-----------------------------
#include <iostream>
#include <cmath>
#include <algorithm>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

using namespace std;

double V[101]; // 状態価値関数
double pi[101];   // 方策
int ct = 1;

void write_graph(const char* name,int n,double* d)
{
int x, y,my;
unsigned char img[100 * 100*4];
char fn[256];
sprintf(fn, "%s%d.png",name, n);
memset(img, 255, sizeof(img));

for (x = 0; x < 100; x++){
my = d[x]*100;
if (my > 100)my = 100;
for (y = 0; y < my; y++){
img[(100 - 1 - y) * 100 * 4 + x * 4] = 0;
img[(100 - 1 - y) * 100 * 4 + x * 4+1] = 0;
img[(100 - 1 - y) * 100 * 4 + x * 4+2] = 255;
img[(100 - 1 - y) * 100 * 4 + x * 4+3] = 255;
}
}
stbi_write_png(fn, 100, 100, 4, img, 4 * 100);
}


int main(void){
const double p = 0.4; //表が出る確率

// 状態価値関数の初期化
for (int s = 0; s<100; ++s) V[s] = 0;
V[100] = 1.0;

const double theta = 1e-5; // ループ終了のしきい値
double delta = 1.0; // 最大変更量

// 状態価値関数の更新
while (delta >= theta){
delta = 0.0;
for (int s = 1; s<100; ++s){
double V_old = V[s];
double cand = 0.0;
// 可能な掛け金ごとに勝率を調べる
for (int bet = 1; bet <= min(s, 100 - s); ++bet){
double tmp = p*V[s + bet] + (1.0 - p)*V[s - bet];
cand = max(tmp, cand);
}
V[s] = cand;
delta = max(delta, abs(V_old - V[s])); // 変更量のチェック
}
//状態価値関数の表示
//for (int i = 0; i<101; i++){
// cout << V[i] << ", ";
//}
//cout << endl << endl;
write_graph("v",ct,V);
ct++;
}

// 最適方策の更新
double threshold = 1e-5;
for (int s = 1; s<100; ++s){
double cand = 0;
double tmp;
for (int bet = 1; bet <= min(s, 100 - s); ++bet){
tmp = p*V[s + bet] + (1 - p)*V[s - bet];
if (tmp > cand + threshold){
cand = tmp;
//pi[s] = bet
pi[s] = (double)bet / 100;
}
}
}
// 最適方策の表示
//for (int i = 1; i<100; i++){
// cout << pi[i] << ", ";
//}
//cout << endl << endl;
write_graph("s",0,pi);
}