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:
en fin de compte, je veux
- pour lisser une séquence de points en utilisant différents noyaux.
- 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 :
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
Epsilon=6
Epsilon=12
Epsilon=24
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
k = 53
k=103
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.
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.
une Autre possibilité est d'utiliser l'algorithme openFrameworks utilise:
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).
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)
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).
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.
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.
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: