2009年03月23日

CのMagickWandでPerlMagickに惨敗

毎朝写真を撮ってココログにアップしているのだが
デジカメからの最初の処理をPerlMagickでやっていた。
しかししょっぱなの起動コストが低いものの作業コストは
PerlMagickと変わらないかやや劣るという結果になった。
惨敗である。
ただし今回はmain関数内にfind_Lに渡すselect関数を置き
main関数内のローカル変数を使用するということをやってみた。
グローバル関数にローカル関数を渡して関数外の変数を使うことが出来た。
リサイズするだけであれば既存のライブラリーとしてgdkなりsdl_imageなどがある。
本来ならそれで充分なのだが現状のデジカメは不調なので
シャープネスはかけないにしろコントラストとガンマ調整は必須なのだ。

perlではFile::Find用のコールバックで

$img = Image::Magick->new();
$img->Read($_);
$img->Modulate(brightness=>95, saturation=>105);
$img->Gamma(gamma=>'0.95', channel=>'Green');
$img->Gamma(gamma=>'1.05');
#$img->UnsharpMask(radius=>5, sigma=>10, amount=>150, threshold=>10);
$img -> Resize(
width => $R_X,
height => $R_Y,
filter=>Lanczos,
blur => 0.9,
);
$img->Set(quality =>80);
$img->Write($o_img);

という流れになる。

MagickWandだとmainで

MagickWandGenesis();

で初期化して

MagickWandTerminus();

で開放するのだが

select内では

img=NewMagickWand();
status =MagickReadImage(img,$->fts_path);
if (status == MagickFalse) WandExcep(img);
MagickModulateImage(img,95,105,100);
MagickGammaImageChannel(img,GreenChannel,0.95);
MagickGammaImage(img,1.05);
//MagickUnsharpMaskImage(img,5,10,150,10);
MagickResizeImage(img,R_X,R_Y,LanczosFilter,0.9);
MagickSetImageCompressionQuality(img,80);
mstatus=MagickWriteImages(img,buf,MagickTrue);
img=DestroyMagickWand(img);


とほぼ等価なのだが複数枚の画像の処理に
Perlが22秒、Cが21〜23秒だった。
Perlは2度目以降の時間なので一回目はPerlの起動コストがかかる。
resizeは同じサイズであっても良い具合にシャープネスをかけられる。

Newとdestroyのコストかと思い

ClearMagickWand(img);

としてNewとdestroyをmainで一回だけ実行する形にしたら38秒になってしまった。
低レベルAPIであるMagickCoreを使う手も考えたがこのままにした。
ソートの分コストがかかっているとはいえ正規表現に対して単純な文字比較を使っている分けだし
実質Magickの部分だけで時間を取っているはずだ。
Perl側はhashで関数にテキストを渡しているわけでそれに負けるというのは憮然とするしかない。

そもそもImageMagickを使おうと思ったのはFreeBSDでしかもCではいきなり作れないと思ったからで
資料は豊富にある。
暫定的に未実装の画像変換を使う場合はMagickWandを使うとしてリサイズと色調あたりから
速いライブラリーを構築したいところだ。
現実問題としてFreeBSD上でもwineでIfranviewを使って同様の事をすると
10sで済む難点はせいぜいコンソールモードで使えないことくらいだ。

今回は前回のfindからレベルを省いたものを使ったのだが全体の流れとしては


#include "fts3.c"
.
.
int main(int argc, char** argv)
{
//const
.
.
//var
MagickWand *img;
char buf[2048];
.
.
.
//begen
.
.
//select function
int select_Jc(FTSENT *$){
//var
char *c;
int l,a,result;
MagickBooleanType status;
//begen
.
.
printf("%s --> %s\n",$->fts_name,buf);//j_nm);

//*/
img=DestroyMagickWand(img);

}
return(0);
}
//end function

MagickWandGenesis();
select=&select_Jc;
if (find_L(path,select)){
puts("fail");
return(1);
}
MagickWandTerminus();
return 0;
}

と分割コンパイルではなくて分割コンパイルも出来るCを作ってそれをインクルードしてしまい宣言を省き
fts.c内にあるfind_Lに対してローカル関数を指定して
ローカル関数内ではmain関数内のローカル変数も使っている。
findというかfts_readに渡す関数はユーザーサイドでの関数宣言なのだから今回の場合は

find_L(path,select_Jc)

で良かった。

この手法ができるならばユーザーサイドの関数内にコールバックを記述して関数に渡すことで
マルチスレッドでも衝突しないコードがかけそうだが
だとするとftwでも使えるともいえそうだ。


posted by Xo_ox at 11:57| Comment(0) | C | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。