14

OpenCV (C++ API) で輪郭のスムージングとサンプリングに頭を悩ませています。ポイントのシーケンスを取得したとしましょうcv::findContours(たとえば、この画像に適用されます:

ここに画像の説明を入力

最終的に、私はしたいです

  1. 異なるカーネルを使用して一連のポイントを滑らかにします。
  2. さまざまな種類の補間を使用してシーケンスのサイズを変更します。

平滑化後、次のような結果が得られることを願っています。

ここに画像の説明を入力

また、 で輪郭を描画しcv::Mat、マットをフィルタリングし (ぼかしまたはモルフォロジー操作を使用)、輪郭を再検出することも検討しましたが、遅くて最適ではありません。したがって、理想的には、点列だけを使用して仕事をすることができました。

私はそれに関するいくつかの投稿を読み、a std::vector(of cv::Point) を aに変換するだけでcv::Mat、blur/resize などの OpenCV 関数が私のために仕事をしてくれると単純に考えましたが、そうではありませんでした。

これが私が試したものです:

int main( int argc, char** argv ){

    cv::Mat conv,ori;
    ori=cv::imread(argv[1]);
    ori.copyTo(conv);
    cv::cvtColor(ori,ori,CV_BGR2GRAY);

    std::vector<std::vector<cv::Point> > contours;
    std::vector<cv::Vec4i > hierarchy;

    cv::findContours(ori, contours,hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);

    for(int k=0;k<100;k += 2){
        cv::Mat smoothCont;

        smoothCont = cv::Mat(contours[0]);
        std::cout<<smoothCont.rows<<"\t"<<smoothCont.cols<<std::endl;
        /* Try smoothing: no modification of the array*/
//        cv::GaussianBlur(smoothCont, smoothCont, cv::Size(k+1,1),k);
        /* Try sampling: "Assertion failed (func != 0) in resize"*/
//        cv::resize(smoothCont,smoothCont,cv::Size(0,0),1,1);
        std::vector<std::vector<cv::Point> > v(1);
        smoothCont.copyTo(v[0]);
        cv::drawContours(conv,v,0,cv::Scalar(255,0,0),2,CV_AA);
        std::cout<<k<<std::endl;
        cv::imshow("conv", conv);
        cv::waitKey();
    }
    return 1;
}

誰でもこれを行う方法を説明できますか?

さらに、私ははるかに小さな輪郭で作業する可能性が高いため、このアプローチが境界効果をどのように処理するのか疑問に思っていました (たとえば、輪郭が円形であるため、平滑化の場合、シーケンスの最後の要素を使用して新しい値を計算する必要があります)最初の要素...)

アドバイスをありがとうございました。

編集:

私も試してみcv::approxPolyDP()ましたが、ご覧のとおり、極値を保持する傾向があります(削除したい):

イプシロン=0

ここに画像の説明を入力

イプシロン=6

ここに画像の説明を入力

イプシロン=12

ここに画像の説明を入力

イプシロン=24

ここに画像の説明を入力

編集 2: Ben が提案したように、サポートされてcv::GaussianBlur()いないようですが、サポートされていcv::blur()ます。それは私の期待に非常に近いように見えます。これを使用した結果は次のとおりです。

k=13

ここに画像の説明を入力

k=53

ここに画像の説明を入力

k=103

ここに画像の説明を入力

境界効果を回避するために、次のことを行いました。

    cv::copyMakeBorder(smoothCont,smoothCont, (k-1)/2,(k-1)/2 ,0, 0, cv::BORDER_WRAP);
    cv::blur(smoothCont, result, cv::Size(1,k),cv::Point(-1,-1));
    result.rowRange(cv::Range((k-1)/2,1+result.rows-(k-1)/2)).copyTo(v[0]);

輪郭を補間/サンプリングするソリューションをまだ探しています。

4

8 に答える 8

2

列方向にぼかしているため、ガウスぼかしは機能しませんが、列は1つだけです。を使用GaussianBlur()すると、ベクターをにコピーして戻そうとすると、OpenCVで「機能が実装されていません」というエラーが発生しますcv::Mat(これが、コードにこの奇妙な点がある理由ですresize())が、を使用するとすべてが正常に機能cv::blur()resize()ます。たとえば、Size(0,41)を試してください。ボーダーの問題に使用cv::BORDER_WRAPすることもうまくいかないようですが、これその回避策を見つけた誰かの別のスレッドです。

ああ...もう1つ:輪郭がはるかに小さくなる可能性が高いとおっしゃいました。そのように輪郭を滑らかにすると、輪郭が縮小します。極端な場合はk = size_of_contour、であり、結果として単一のポイントになります。したがって、kを大きくしすぎないでください。

于 2012-08-13T13:00:40.090 に答える
2

もう 1 つの可能性は、openFrameworks が使用するアルゴリズムを使用することです。

https://github.com/openframeworks/openFrameworks/blob/master/libs/openFrameworks/graphics/ofPolyline.cpp#L416-459

輪郭を横断し、本質的にその周囲のポイントを使用してローパス フィルターを適用します。オーバーヘッドを低く抑えて、必要なことを正確に実行する必要があります (本質的に単なる輪郭である画像に大きなフィルターを適用する理由はありません)。

于 2015-01-06T18:36:03.710 に答える
1

approxPolyDP()はどうですか?

このアルゴリズムを使用して、輪郭を「滑らかに」します (基本的に、輪郭のポイントのほとんどを取り除き、輪郭の適切な近似を表すポイントを残します)。

于 2012-08-13T09:30:40.253 に答える
0

2.1 OpenCVドキュメントセクションの基本構造から:

template<typename T>
explicit Mat::Mat(const vector<T>& vec, bool copyData=false)

おそらく、2番目のパラメータを次のように設定する必要がtrueあります。

smoothCont = cv::Mat(contours[0]);

もう一度やり直してください(この方法cv::GaussianBlurでデータを変更できるはずです)。

于 2012-08-13T09:24:13.970 に答える
0

私はこれがずっと前に書かれたことを知っています. シンプルで高速なソリューションのように見えますが、少なくともある程度は機能すると思います。

于 2013-05-11T18:26:07.193 に答える