OpenCV, comment utiliser des matrices de points pour lisser et échantillonner les contours?

j'ai un problème pour obtenir ma tête autour du lissage et des contours d'échantillonnage dans OpenCV (API C++). Disons que j'ai une séquence de points extraits de cv::findContours (par exemple appliqué sur cette image:

enter image description here

en fin de compte, je veux

  1. pour lisser une séquence de points en utilisant différents noyaux.
  2. pour redimensionner la séquence en utilisant différents types d'interpolations.

après lissage, I l'espoir d'avoir un résultat de la forme :

enter image description here

j'ai aussi envisagé de dessiner mon contour dans un cv::Mat, filtrant le tapis (par des opérations floues ou morphologiques) et retrouvant les contours, mais est lent et sous-optimal. Donc, idéalement, je pourrais faire le travail en utilisant exclusivement la séquence de points.

j'ai lu quelques posts sur elle et pensais naïvement que je pourrais simplement de convertir un std::vector(cv::Point)cv::Mat et puis OpenCV fonctionne comme flou / redimensionner pour faire le travail pour moi... mais ils n'ont pas.

Voici ce que j'ai essayé:

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

quelqu'un Pourrait-il expliquer comment le faire ?

de plus, étant donné que je suis susceptible de travailler avec des contours beaucoup plus petits, je me demandais comment cette approche traiterait l'effet de bordure (p. ex. lors du lissage, puisque les contours sont circulaires, les derniers éléments d'une séquence doivent être utilisés pour calculer la nouvelle valeur des premiers éléments...)

je vous Remercie beaucoup pour vos conseils,

Edit:

j'ai aussi essayé cv::approxPolyDP() mais, comme vous pouvez le voir, il tend à préserver les points extrêmes (que je veux supprimer):

Epsilon=0

enter image description here

Epsilon=6

enter image description here

Epsilon=12

enter image description here

Epsilon=24

enter image description here

Modifier 2: Comme L'a suggéré Ben, il semble que cv::GaussianBlur() n'est pas supporté mais cv::blur() est. Il semble beaucoup plus proche de mes attentes. Voici mes résultats en l'utilisant:

k=13

enter image description here

k = 53

enter image description here

k=103

enter image description here

Pour obtenir autour de la frontière effet, j'ai fait:

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

je suis toujours à la recherche de solutions pour interpoler/échantillonner mon contour.

11
demandé sur Quentin Geissmann 2012-08-13 00:48:12

7 réponses

votre flou gaussien ne fonctionne pas parce que vous floutez dans le sens de la colonne, mais il n'y a qu'une colonne. En utilisant GaussianBlur() conduit à une erreur" feature not implemented " dans OpenCV lors de la copie du vecteur vers un cv::Mat (c'est probablement pour cela que vous avez cette étrange resize() dans votre code), mais tout fonctionne très bien avec cv::blur(), pas besoin de resize(). Essayez la taille (0,41) Par exemple. En utilisant cv::BORDER_WRAP pour la question de la frontière ne semble pas fonctionner non plus, mais ici est un autre le fil de quelqu'un qui a trouvé une solution pour ça.

Oh... encore une chose: vous avez dit que les contours sont susceptibles d'être beaucoup plus petit. Lisser votre contour de cette façon le rétrécira. Le cas extrême est k = size_of_contour, ce qui donne un seul point. Alors ne choisis pas ton k trop grand.

2
répondu Ben 2017-05-23 12:00:00

une Autre possibilité est d'utiliser l'algorithme openFrameworks utilise:

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

il traverse le contour et applique essentiellement un filtre passe-bas en utilisant les points qui l'entourent. Devrait faire exactement ce que vous voulez avec un plafond bas et (il n'y a aucune raison de faire un grand filtre sur une image qui est essentiellement juste un contour).

2
répondu Aaron Krajeski 2015-01-06 18:36:03

Que Diriez-vous de approxPolyDP ()?

algorithme pour "lisser" un contour (essentiellement gettig débarrasser de la plupart des points du contour et laisser ceux qui représentent une bonne approximation de votre contour)

0
répondu Ben 2012-08-13 09:30:40

From 2.1 OpenCV doc section Structures De Base:

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

vous voulez probablement mettre 2nd param à true en:

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

et essayez à nouveau (de cette façon cv::GaussianBlur devrait pouvoir modifier les données).

0
répondu weidongxu 2012-08-13 20:34:12

je sais que cela a été écrit il y a longtemps, mais avez-vous essayé une grande érosion suivie d'une grande dilatation (ouverture), et ensuite trouver les comtours? Cela ressemble à une solution simple et rapide, mais je pense que cela pourrait fonctionner, au moins dans une certaine mesure.

0
répondu jmacedo 2013-05-11 18:26:07

fondamentalement, les changements soudains de contour correspondent à des fréquences élevées. Un moyen facile de lisser votre contour serait de trouver les coefficients de fourier en supposant que les coordonnées forment un plan complexe x + iy et puis en éliminant les coefficients de haute fréquence.

0
répondu user3540823 2015-08-26 16:06:53

ma prise ... de nombreuses années plus tard ...!

Peut-être deux façons de faire:

  • boucle plusieurs fois avec dilate,flou, érode. Et trouver les contours sur cette nouvelle forme. J'ai trouvé 6-7 fois donne de bons résultats.
  • créer une boîte de délimitation du contour, et dessiner une ellipse à l'intérieur du rectangle délimité.

Ajout d'un visuel résultats ci-dessous:

enter image description here

0
répondu Nicolas Modrzyk 2017-07-25 06:21:32