Thrift + OpenCV
Thriftを利用してOpenCVの関数をスクリプトから呼んでみます。
といってもRPCで繋げるだけで特別なことをする必要はありません。
ここでは、画像の各チャンネルの平均値と標準偏差を求めてみます。
サーバ側でOpenCVの cvAvgSvd() を利用すればOK。
まず、最初にRPCのインタフェースをThrift IDLに書きます。
ここでは、平均と標準偏差を求める関数を2つ作ることにしました。
各チャンネルの値が欲しいので戻り値は list<double> に、
引数は画像ファイル名を指定したいので string にしておきます。
• cv.thrift (サービス名は CV)
service CV {
list<double> calcAvg(1: string img)
list<double> calcSdv(1: string img)
}
• thriftコマンドでコード生成
$ thrift --gen cpp --gen perl --gen php --gen rb cv.thrift
サーバ側の実装ではC++で普通にOpenCVを使って書けばOKです。
(今回の場合は gen-cpp/CV_server.skeleton.cpp というファイルを編集する)
メソッドの引数にだけ注意しておきます。
ここではコードを一部省略しますが、実際はドメインロジック以外自動生成されます。
ソケットを作って接続を待ったり、データ構造をシリアライズしたりといった面倒な部分は、
全てThriftが引き受けてくれるので安心です。
• サーバ側 CV_server.skeleton.cpp (C++)
#include "CV.h"
#include <protocol/TBinaryProtocol.h>
#include <server/TSimpleServer.h>
#include <transport/TServerSocket.h>
#include <transport/TBufferTransports.h>
#include <cv.h>
#include <highgui.h>
#include <iostream>
/* 中略 */
class CVHandler : virtual public CVIf {
IplImage *srcImg;
CvScalar mean;
CvScalar std_dev;
public:
CVHandler() {
srcImg = 0;
mean = cvScalarAll(0);
std_dev = cvScalarAll(0);
}
void calcAvg(std::vector<double> & _return, const std::string& img) {
if((srcImg = cvLoadImage(img.c_str(), CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR)) == 0) {
std::cout<<"load error."<<std::endl;
exit(0);
}
cvAvgSdv(srcImg , &mean, NULL);
for(int i=0;i<3;++i) _return.push_back(mean.val[i]);
cvReleaseImage(&srcImg);
}
void calcSdv(std::vector<double> & _return, const std::string& img) {
/* 以下略 (calcAvgと同様に書けばよい)*/
• コンパイル
$ g++ -g CV.cpp CV_server.skeleton.cpp -o CV_server `pkg-config --cflags --libs opencv thrift`
次はクライアント側を実装します。
• クライアント側 cv.pl (Perl)
#!/usr/bin/perl
use strict;
use warnings;
use lib './gen-perl';
use Thrift;
use Thrift::BinaryProtocol;
use Thrift::Socket;
use CV;
my $transport = Thrift::Socket->new('localhost', 9090);
my $client = CVClient->new(Thrift::BinaryProtocol->new($transport));
eval {
$transport->open();
print "Calculate the Mean\n( Blue Green Red ) = ( ";
printf "%f ", $_ for @{$client->calcAvg("lena.jpg")}; # 戻り値はリストリファレンス
print ")\n\n";
print "Calculate the Standard-Deviation\n( Blue Green Red ) = ( ";
printf "%f ", $_ for @{$client->calcSdv("baboon.jpg")};
print ")";
$transport->close();
};
if($@) {
warn $@->{message};
}
• サーバの起動およびクライアントの実行
$ mv ./gen-cpp/CV_server . $ ./CV_server
$ chmod +x cv.pl $ ./cv.pl Calculate the Mean ( Blue Green Red ) = ( 105.398991 99.562698 179.730305 ) Calculate the Standard-Deviation ( Blue Green Red ) = ( 60.611124 47.493925 55.499225 )
上手くいきました。
次はPHPから。抽象化レイヤのおかげでPHPでも簡単に使えます。
• クライアント側 cv.php (PHP)
<?php
$GLOBALS['THRIFT_ROOT'] = 'src';
$GEN_DIR = 'gen-php';
/** Include the Thrift base */
require_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php';
/** Include the binary protocol */
require_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php';
/** Include the socket layer */
require_once $GLOBALS['THRIFT_ROOT'].'/transport/TSocketPool.php';
/** Include the socket layer */
require_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php';
/** Include the generated code */
require_once $GEN_DIR.'/CV.php';
require_once $GEN_DIR.'/cv_types.php';
$transport = new TSocket('localhost', 9090);
$client = new CVClient(new TBinaryProtocol($transport));
try {
$transport->open();
print_r($client->calcAvg("lena.jpg")); /* Perl版と同じように呼べる */
print_r($client->calcSdv("baboon.jpg"));
$transport->close();
}catch(Exception $e) {
echo $e->getMessage();
}
?>
• 出力結果
Array
(
[0] => 105.3989906311
[1] => 99.562698364258
[2] => 179.73030471802
)
Array
(
[0] => 60.611123950567
[1] => 47.493925313866
[2] => 55.499224912776
)
詳細は省きますが、Rubyでもシンタックスを変えるだけで利用できます。
# Rubyでも簡単
client.calcAvg("lena.jpg")
client.calcSdv("baboon.jpg")
今回はThriftを使って各言語から簡単にOpenCVを利用できることを確認しました。
それはともかくOpenCV 2.0 のリリースが Sept. 31, 2009 って書いてありましたが、
また便利な機能が追加されてるんですかね。楽しみです。


Comments
No comments so far.