OpenCV輪郭抽出ではまりがちなこと
先日展示会のために作成したプログラムではたくさん輪郭抽出しています。
そのときはまってしまった恥ずかしいポイントをまとめておきます。
OpenCVで輪郭抽出するサンプルプログラム
サンプルプログラムはこのあたりを参考になさってください
opencv.jp - OpenCV: 木(Trees)サンプルコード -
cvFindContours (tmp_img, storage, &contours, sizeof (CvContour), CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
うん、輪郭抽出するのは、これだけなんです。これだけ。
白黒2値化されたtmp_imgの輪郭シーケンスをCvSeq型のcontoursに抽出します。
問題が発生する条件
輪郭を抽出するからには、輪郭に何らかの処理を加えたいと思うのが世の常です。
特に、プログラム中で何度も何度も輪郭をとることのほうが多いと思います。
このときに、CvSeqがCvMemStorageのなかに動的確保されていることを忘れると悲惨なことが起こります。
悲惨なこと=メモリ関係の再現性のないバグが発生します。
- どちらかの解放を忘れるとメモリがいっぱいになって落ちるプログラムが生産できる。
- どちらかの解放をむやみに行うと再現性のないバグが発生し路頭に迷う。
当たり前といえば当たり前ですが、私ははまってしまった・・・・・!!!*1
問題を解決する
適切にCvSeqとCvMemStorageを確保/解放してあげればよい、ということになります。
まずは以下を読んで、メモリストレージについて理解を深めます。
opencv.jp - OpenCV-1.0:CXCORE メモリストレージ(Memory Storages)リファレンス マニュアル -
リファレンスによれば
CvMemStorage *storage = cvCreateMemStorage (0);
で使ったストレージ(親ストレージ)を使いまわして、CvSeqを確保/解放すると、メモリ領域に穴ができるらしい。
http://opencv.jp/opencv-1.0.0/document/opencvref_cxcore_storages.html
これを避けるためにはcvCreateChildMemStorageという関数を使って子ストレージを作ればよいらしい
CvMemStorage* cvCreateChildMemStorage( CvMemStorage* parent );
http://opencv.jp/opencv-1.0.0/document/opencvref_cxcore_storages.html
というわけで、プログラムをまとめると
void main(){ ・・・ CvMemStorage *storage = cvCreateMemStorage (0); ・・・ useContours(storage); ・・・ cvReleaseMemStorage(&storage ); } void useContours(CvMemStorage *parent_storage){ CvMemStorage* child_storage = cvCreateChildMemStorage(parent_storage ); CvSeq *contours = 0; ・・・ cvFindContours (tmp_img, child_storage, &contours, sizeof (CvContour), CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE); ・・・ 輪郭を使って何かする ・・・ cvReleaseSeq(&contours) cvReleaseMemStorage(&child_storage );
まとめ
プログラム中で複数回 cvFindContoursを使う場合は、1つ親ストレージを作った後、cvFindContoursのたびに子ストレージをCreateChildMemStorageで作っては捨て、子供を作っては捨てするとよい。
参考文献
これにおせわになりました。ありがとうありがとう。
詳解 OpenCV ―コンピュータビジョンライブラリを使った画像処理・認識
- 作者: Gary Bradski,Adrian Kaehler,松田晃一
- 出版社/メーカー: オライリージャパン
- 発売日: 2009/08/24
- メディア: 単行本(ソフトカバー)
- 購入: 17人 クリック: 272回
- この商品を含むブログ (34件) を見る