読者です 読者をやめる 読者になる 読者になる

毛のはえたようなもの

インターネット的なものをつらつらとかきつらねる。

OpenCV輪郭抽出ではまりがちなこと

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/pics/memstorage1.png

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/pics/memstorage2.png

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 );

*2

まとめ

プログラム中で複数回 cvFindContoursを使う場合は、1つ親ストレージを作った後、cvFindContoursのたびに子ストレージをCreateChildMemStorageで作っては捨て、子供を作っては捨てするとよい。

参考文献

これにおせわになりました。ありがとうありがとう。

詳解 OpenCV ―コンピュータビジョンライブラリを使った画像処理・認識

詳解 OpenCV ―コンピュータビジョンライブラリを使った画像処理・認識

*1:ちなみにこのバグは、久しぶりにもなえデバッガを起動したので解決された。もなえ氏、OpenCVさわったことないのに…もなえデバッガいつもありがとうありがとう。

*2:cvReleaseSeqはOpenCVリファレンスに載っていない。cvReleaseMemStorageするときに勝手に実行されている気もするので、しなくてもいいかもしれない。ちょっと調べておきます。