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 って書いてありましたが、
また便利な機能が追加されてるんですかね。楽しみです。

 

Tags: , , ,

Comments

No comments so far.

  • Leave a Reply
     
    Your gravatar
    Your Name