CV系のライブラリだと、OpenCVが圧倒的にシェア率が高く、使いやすいが、最近「CCV」というライブラリを使う機会があったので、紹介したいと思います。
CCVとは
C/C++用のコンピュータービジョン系のライブラリです。
http://libccv.org/
OpenCVが1999年から使われ始めたのに対し、CCVは2010年に誕生したそうです。
特徴として、
-
マルチプラットフォームで動く。
-
最先端の画像認識アルゴリズムが使われている。
-
インタフェースがクリーンで分かりやすい。
みたいな感じです。 -
Track Learn Detect(物体の追跡認識)
-
Stroke Width Transform(画像からの文字抽出)
-
Conv Net(畳み込みニューラルネットワーク)
-
Integral Channel Features(輝度、色、スペクトラムなど多くの情報から認識を行うアルゴリズム)
など、まあまあ近年の論文で発表されて、中にはOpenCVにはデフォルトでは実装されていないアルゴリズムが、きれいな関数で実装されています。
僕は使ってないのでわからないのですが、HTTP用のREST-ful APIも用意されているみたいで、サービスでの応用もやりやすそうです。(ライセンスがBSDなのでご注意ください。)
学習モデルは、使用するときに必ずSQLite形式のデータベースファイルの読み込み処理が必要になります。
上で挙げた機能や、顔認識、ImageNetやAlexNet用のモデルはSampleからダウンロードできます。
自分でモデルを実装したい場合は、ここを参考にしてください。
ここでは、デフォルトで提供されているモデルを使って、応用の仕方を説明していきます。
みんな大好きopenFrameworksで使ってみる
なんとKyle Mcdonaldさんが、oFのAddonを作ってくれていました。
https://github.com/kylemcdonald/ofxCcv
今回はopenFrameworksでこのアドオンを使って、手軽に試してみます。
歩行者認識(Integral Channel Featuresを使った実装方法)
classifyPedestrians()
を使用します。
認識するためのコードは以下のたったこれだけです。
#include "ofMain.h"
#include "ofxCcv.h"
class ofApp : public ofBaseApp {
public:
ofxCcv ccv;
vector<ofRectangle> results;
ofVideoPlayer player;
void setup() {
ccv.setupPedestrians("pedestrian.icf");
player.load("movie.mp4");
player.play();
}
void update() {
player.update();
if(player.isFrameNew()) {
results = ccv.classifyPedestrians(player);
}
}
void draw() {
player.draw(0, 0);
ofSetWindowTitle(ofToString(ofGetFrameRate()));
ofPushStyle();
ofNoFill();
ofSetLineWidth(2);
for(int i = 0; i < results.size(); i++) {
ofDrawRectangle(results[i].x,results[i].y,results[i].getWidth(),results[i].getHeight());
ofDrawBitmapStringHighlight(ofToString(i), results[i].getPosition() + ofPoint(-5,-5));
}
ofPopStyle();
}
};
int main() {
ofSetupOpenGL(1200, 800, OF_WINDOW);
ofRunApp(new ofApp());
}
すると、
ちゃんと取れてますね。めちゃ手軽です。
pedestrian.icfはモデルデータです。ofxCcvのサンプルの中に落ちています。
ただし、本格的に使う場合は、ccv.classifyPedestrians()はスレッドを分けて使う必要がありそうです。(実行するときにフレームが1~2まで落ちてしまう。)
顔認識
classifyFace()
を使用します。
これもコードが以下のようにとても少なくすみます。
#include "ofMain.h"
#include "ofxCcv.h"
class ofApp : public ofBaseApp {
public:
ofxCcv ccv;
vector<ofRectangle> results;
ofImage image;
void setup() {
ccv.setupFace("face.sqlite3");
image.load("image.jpg");
}
void update() {
if(ofGetKeyPressed()) {
results = ccv.classifyFace(image);
}
}
void draw() {
image.draw(0, 0);
ofPushStyle();
ofNoFill();
ofSetLineWidth(2);
ofPushStyle();
ofSetColor(0, 0, 255);
for(int i = 0; i < results.size(); i++) {
ofDrawRectangle(results[i]);
ofDrawBitmapStringHighlight(ofToString(i), results[i].getPosition() + ofPoint(-5,-5));
}
ofPopStyle();
ofPopStyle();
ofDrawBitmapStringHighlight("press any key to detect", 10,20);
}
};
int main() {
ofSetupOpenGL(1140, 641, OF_WINDOW);
ofRunApp(new ofApp());
}
畳みこみニューラルネット
これは、少々コードが多くなるので、処理の根幹の部分だけ書きます。
ofxCcvの中に、example-viewerというプロジェクトがあり、そのコードで説明していきます。
ofxCcv ccv; //ofxCcvのインスタンス
ccv.setup("image-net-2012.sqlite3");
/*
CNNのモデルの読み込み
*/
ccv.encode(image, ccv.numLayers());
/*
画像を入力し、認識情報を更新する。
- image: ofPixels(入力画像)
*/
ccv.getFeatureMaps(layer_num)
/*
指定した番号のレイヤー情報を、FeatureMapとして取得する。
- layer_num: int(レイヤーの番号)
*/
// Feature Map
class FeatureMap {
public:
vector<float> acts;
int rows;
int cols;
float max;
void getImage(ofImage & img, bool autoBrighten=true);
};
こういう感じで、ネットワーク内での畳みこみ画像を取得し、表示することもできます。
自分でモデルを作るのはかなりしんどいですが、IamgeNetなどの既存のモデルを動かすだけなら、実質
ccv.encode(image, ccv.numLayers())
だけで動きます。
その他
ml4a-ofxというプロジェクトがあり、ofxCcvのサンプルが使いやすいようにまとめられています。
雑感
既存の認識処理モデルを動かすのは、とてもシンプルに実装できて好きでした。
CaffeやTensorflowで作ったモデルを.sqlite3に直して、Ccvで実行するみたいなことができるといいなと思いました。
時間のあるときに、文字抽出とhttpAPIのインターフェースをofxCcvに実装しようと思います