OpenCV, comment utiliser des tableaux de points pour lisser et échantillonner les contours?
J'ai un problème pour obtenir ma tête autour de lissage et d'échantillonnage des contours dans OpenCV (API C++).
Disons que j'ai eu de la séquence de points récupérés à partir de cv::findContours
(par exemple appliqué sur cette image:
En fin de compte, je veux
- Pour lisser une séquence de points à l'aide de différents noyaux.
- Pour redimensionner la séquence à l'aide de différents types d'interpolations.
Après le lissage, j'espère avoir un résultat comme :
J'ai également considéré le dessin de mon contour dans un cv::Mat
le filtrage, le Tapis (à l'aide de flou ou les opérations morphologiques) et de re-trouver les contours, mais il est lent et sous-optimale. Donc, idéalement, je pouvais faire le travail en utilisant exclusivement le point de la séquence.
J'ai lu quelques posts sur elle et pensais naïvement que je pourrais simplement de convertir un std::vector
(de cv::Point
) à un cv::Mat
et puis OpenCV fonctions comme le flou ou le redimensionnement de 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 faire cela ?
En outre, depuis que j'ai la chance de travailler avec beaucoup de plus petits contours, je me demandais comment cette approche permettrait de traiter avec l'effet de bordure (par exemple, lorsque le lissage, car les contours sont circulaires, les derniers éléments d'une séquence doit être utilisée pour calculer la nouvelle valeur de la première éléments...)
Merci beaucoup pour vos conseils,
Edit:
J'ai aussi essayé cv::approxPolyDP()
mais, comme vous pouvez le voir, elle tend à préserver extrémal points (qui je veux supprimer):
Epsilon=0
Epsilon=6
Epsilon=12
Epsilon=24
Edit 2:
Comme suggéré par Ben, il semble que cv::GaussianBlur()
n'est pas pris en charge, mais cv::blur()
est. Il a l'air 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/exemple de mon contour.
source d'informationauteur Quentin Geissmann
Vous devez vous connecter pour publier un commentaire.
Votre flou Gaussien ne fonctionne pas parce que vous êtes le flou dans le sens de la colonne, mais il y a une seule colonne. À l'aide de
GaussianBlur()
conduit à une "fonctionnalité qui n'est pas mis en œuvre" erreur dans OpenCV lorsque vous essayez de copier le vecteur de retour d'uncv::Mat
(c'est sans doute pourquoi vous avez cette étrangeresize()
dans votre code), mais tout fonctionne bien à l'aide decv::blur()
pas besoin deresize()
. Essayez de Taille(0,41) par exemple. À l'aide decv::BORDER_WRAP
pour la question de la frontière ne semble pas fonctionner non plus, mais ici est un fil de quelqu'un qui a trouvé une solution de contournement pour que.Oh... encore une chose: vous avez dit que les contours sont susceptibles d'être beaucoup plus petit. Le lissage du contour de cette manière que de la réduire. Le cas extrême est
k = size_of_contour
ce qui résulte en un seul point. Donc ne choisissez pas votre k trop grand.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 parcourt le contour et s'applique essentiellement un filtre passe-bas à l'aide de points autour d'elle. Faire exactement ce que vous voulez avec une faible surcharge et (il n'y a pas de raison de faire un gros filtre sur une image qui est essentiellement juste un contour).
Comment sur approxPolyDP()?
Il utilise cette algorithme de "lisse" un contour (en gros gettig débarrasser de la plupart des lignes de points et de laisser ceux qui représentent une bonne approximation de votre contour)
De 2,1 OpenCV doc section Structures De Base:
Vous voulez probablement pour définir 2ème paramètre à
true
:et essayez à nouveau (cette façon
cv::GaussianBlur
devrait être en mesure de modifier les données).Je sais que cela a été écrit il y a longtemps, mais avez-vous essayé un grand éroder suivie par un grand dilater (l'ouverture), et ensuite trouver le countours? Il ressemble à une solution simple et rapide, mais je pense que ça pourrait fonctionner, au moins à un certain degré.
Fondamentalement, les changements soudains dans le contour correspond à la haute fréquence, le contenu. Un moyen facile de lisser votre contour devrait être de trouver les coefficients de fourier en supposant que les coordonnées former un plan complexe x + iy et puis en éliminant la fréquence élevée des coefficients.
De mon point de vue ... bien des années plus tard ...!
Peut-être deux façons de le faire:
Ajoutant les résultats visuels ci-dessous: