Thriftを利用してOpenCVの関数をスクリプトから呼んでみます。
といってもRPCで繋げるだけで特別なことをする必要はありません。
ここでは、画像の各チャンネルの平均値と標準偏差を求めてみます。
サーバ側でOpenCVの cvAvgSvd() を利用すればOK。
まず、最初にRPCのインタフェースをThrift IDLに書きます。
ここでは、平均と標準偏差を求める関数を2つ作ることにしました。
各チャンネルの値が欲しいので戻り値は list<double> に、
引数は画像ファイル名を指定したいので string にしておきます。
* cv.thrift (サービス名は CV)
1 2 3 4 |
service CV { list<double> calcAvg(1: string img) list<double> calcSdv(1: string img) } |
* thriftコマンドでコード生成
1 |
$ 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++)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#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と同様に書けばよい)*/ |
* コンパイル
1 |
$ g++ -g CV.cpp CV_server.skeleton.cpp -o CV_server `pkg-config --cflags --libs opencv thrift` |
次はクライアント側を実装します。
* クライアント側 cv.pl (Perl)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#!/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}; } |
* サーバの起動およびクライアントの実行
1 2 |
$ mv ./gen-cpp/CV_server . $ ./CV_server |
1 2 3 4 5 6 7 |
$ 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)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?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(); } ?> |
* 出力結果
1 2 3 4 5 6 7 8 9 10 11 12 |
Array ( [0] => 105.3989906311 [1] => 99.562698364258 [2] => 179.73030471802 ) Array ( [0] => 60.611123950567 [1] => 47.493925313866 [2] => 55.499224912776 ) |
詳細は省きますが、Rubyでもシンタックスを変えるだけで利用できます。
1 2 3 |
# Rubyでも簡単 client.calcAvg("lena.jpg") client.calcSdv("baboon.jpg") |
今回はThriftを使って各言語から簡単にOpenCVを利用できることを確認しました。
それはともかくOpenCV 2.0 のリリースが Sept. 31, 2009 って書いてありましたが、
また便利な機能が追加されてるんですかね。楽しみです。