OpenCV findContours関数のクラッシュの真の有効な解決策

4914 ワード

最近WindowsプラットフォームでOpenCVのfindContours関数を使用すると、クラッシュの問題が発生します.
ネット上で多くの解決策を探しました.例えば:1.属性の構成->一般->プロジェクトのデフォルト->MFCの使用->共有DLLでのMFCの使用2.C/C+>コード生成->ライブラリの実行->マルチスレッドDLL(/MD);3.コードのvectorはcv::vectorを使用し、vectorは事前に空間を割り当てなければならない.
私は列挙しないで、多くのパートナーが上記の方法を使って問題を解決することができないと信じています.あるいは、個別のパートナーはたまたま問題を解決することができますが、プログラムの移植性にはリスクがあります.
次に、私は本当にこの問題を解決できる案を提供します.
問題の原因
なぜfindContoursに異常が発生したのかを理解する必要があります.findContoursのソースコードを見てみましょう.
void cv::findContours( InputOutputArray _image, OutputArrayOfArrays _contours,
                   OutputArray _hierarchy, int mode, int method, Point offset )
{
    Mat image = _image.getMat();
    MemStorage storage(cvCreateMemStorage());
    CvMat _cimage = image;
    CvSeq* _ccontours = 0;
    if( _hierarchy.needed() )
        _hierarchy.clear();
    cvFindContours(&_cimage, storage, &_ccontours, sizeof(CvContour), mode, method, offset);
    if( !_ccontours )
    {
        _contours.clear();
        return;
    }
    Seq all_contours(cvTreeToNodeSeq( _ccontours, sizeof(CvSeq), storage ));
    int i, total = (int)all_contours.size();
    _contours.create(total, 1, 0, -1, true);
    SeqIterator it = all_contours.begin();
    for( i = 0; i < total; i++, ++it )
    {
        CvSeq* c = *it;
        ((CvContour*)c)->color = (int)i;
        _contours.create((int)c->total, 1, CV_32SC2, i, true);
        Mat ci = _contours.getMat(i);
        CV_Assert( ci.isContinuous() );
        cvCvtSeqToArray(c, ci.data);
    }

    if( _hierarchy.needed() )
    {
        _hierarchy.create(1, total, CV_32SC4, -1, true);
        Vec4i* hierarchy = _hierarchy.getMat().ptr();

        it = all_contours.begin();
        for( i = 0; i < total; i++, ++it )
        {
            CvSeq* c = *it;
            int h_next = c->h_next ? ((CvContour*)c->h_next)->color : -1;
            int h_prev = c->h_prev ? ((CvContour*)c->h_prev)->color : -1;
            int v_next = c->v_next ? ((CvContour*)c->v_next)->color : -1;
            int v_prev = c->v_prev ? ((CvContour*)c->v_prev)->color : -1;
            hierarchy[i] = Vec4i(h_next, h_prev, v_next, v_prev);
        }
    }
}

まず、findContours内部で実際に呼び出されたのはcvFindContoursという関数であり、findContoursが入出力データ構造を再カプセル化しただけであることがわかります.findContoursが異常を使用している場合、cvFindContoursをテストして、プログラムが正常であることを発見しました.これは私に大きな自信を与えた.次にfindContoursの関数をよく読むと、次のコードが表示されます.
int i, total = (int)all_contours.size();
_contours.create(total, 1, 0, -1, true);

および
 CvSeq* c = *it;
((CvContour*)c)->color = (int)i;
_contours.create((int)c->total, 1, CV_32SC2, i, true);
Mat ci = _contours.getMat(i);

cvCvtSeqToArray(c, ci.data);

私たちはfindContoursを使うときに慣れています.contoursパラメータはvectorタイプを書きます.しかし、上記のコードからopencvが割り当てられていることがわかりました.contoursメモリ領域の場合,動的作成配列に乱暴に解くだけである.特に、opencvは、ポイントセットデータを割り当てるときに_contoursの各vectorはMatデータ構造として扱われ,その後直接粗暴にデータ充填が行われる.
以上、私の推測では、contours.create()は必ずしもvectorのメモリ割り当てに適しているとは限らないか、vectorのデータが連続メモリ空間として簡単に理解できず、簡単なデータコピー充填を行う.最終的にメモリが破壊され、動作に異常が発生しました.
解決策
前述したように、findContours使用は異常ですが、cvFindContours使用は完全にOKです.では、cvFindContoursを再利用してカプセル化し、前述したリスクのあるメモリ操作を回避すればいいのではないでしょうか.
くだらないことは言わないで、直接コードを貼ってください.ちゃんと持ってくれてありがとう.
void findContours(const Mat& src, vector>& contours, vector& hierarchy,
	int retr = RETR_LIST, int method = CHAIN_APPROX_SIMPLE, Point offset = Point(0, 0))
{
	CvMat c_image = src;
	MemStorage storage(cvCreateMemStorage());
	CvSeq* _ccontours = 0;
	cvFindContours(&c_image, storage, &_ccontours, sizeof(CvContour), retr, method, CvPoint(offset));

	if (!_ccontours)
	{
		contours.clear();
		return;
	}
	Seq all_contours(cvTreeToNodeSeq(_ccontours, sizeof(CvSeq), storage));
	int total = (int)all_contours.size();
	contours.resize(total);

	SeqIterator it = all_contours.begin();
	for (int i = 0; i < total; i++, ++it)
	{
		CvSeq* c = *it;
		((CvContour*)c)->color = (int)i;
		int count = (int)c->total;
		int* data = new int[count * 2];
		cvCvtSeqToArray(c, data);
		for (int j = 0; j < count; j++)
		{
			contours[i].push_back(Point(data[j * 2], data[j * 2 + 1]));
		}
		delete[] data;
	}

	hierarchy.resize(total);
	it = all_contours.begin();
	for (int i = 0; i < total; i++, ++it)
	{
		CvSeq* c = *it;
		int h_next = c->h_next ? ((CvContour*)c->h_next)->color : -1;
		int h_prev = c->h_prev ? ((CvContour*)c->h_prev)->color : -1;
		int v_next = c->v_next ? ((CvContour*)c->v_next)->color : -1;
		int v_prev = c->v_prev ? ((CvContour*)c->v_prev)->color : -1;
		hierarchy[i] = Vec4i(h_next, h_prev, v_next, v_prev);
	}
	storage.release();
}

宣言
これは私の2019年2月26日の第1篇のオリジナルのブログで、もし転載するならば、出典を明記してください、あなた达を爱します.