Remplir des trous à l'intérieur d'un objet binaire
j'ai un problème avec le remplissage des trous blancs à l'intérieur d'une pièce de monnaie noire de sorte que je ne peux avoir que 0-255 image binaire avec des pièces de monnaie noires remplies.. J'ai utilisé le filtre médian pour l'accomplir mais dans ce cas le pont de connexion entre les pièces se développe et il va impossible de les reconnaître après plusieurs fois d'érosion... Donc j'ai besoin d'une méthode simple comme floodFill dans opencv
Voici mon image avec des trous:
EDIT: la fonction floodfill like doit remplir les trous dans les gros composants sans demander X,Y coordonnées comme une graine...
EDIT: j'ai essayé d'utiliser la fonction cvDrawContours mais je ne remplis pas les contours à l'intérieur des plus grands.
Voici mon code:
CvMemStorage mem = cvCreateMemStorage(0);
CvSeq contours = new CvSeq();
CvSeq ptr = new CvSeq();
int sizeofCvContour = Loader.sizeof(CvContour.class);
cvThreshold(gray, gray, 150, 255, CV_THRESH_BINARY_INV);
int numOfContours = cvFindContours(gray, mem, contours, sizeofCvContour, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
System.out.println("The num of contours: "+numOfContours); //prints 87, ok
Random rand = new Random();
for (ptr = contours; ptr != null; ptr = ptr.h_next()) {
Color randomColor = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat());
CvScalar color = CV_RGB( randomColor.getRed(), randomColor.getGreen(), randomColor.getBlue());
cvDrawContours(gray, ptr, color, color, -1, CV_FILLED, 8);
}
CanvasFrame canvas6 = new CanvasFrame("drawContours");
canvas6.showImage(gray);
résultat: (vous pouvez voir les trous noirs à l'intérieur de chaque pièce)
7 réponses
il y a deux méthodes pour faire cela:
1) Garniture De Contour:
inversez D'abord l'image,Trouvez les contours de l'image, remplissez-la de noir et inversez en arrière.
des = cv2.bitwise_not(gray)
contour,hier = cv2.findContours(des,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
for cnt in contour:
cv2.drawContours(des,[cnt],0,255,-1)
gray = cv2.bitwise_not(des)
image résultante:
2) Ouverture De L'Image:
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
res = cv2.morphologyEx(gray,cv2.MORPH_OPEN,kernel)
l'image résultante comme suit:
vous pouvez voir, il n'y a pas beaucoup de différence dans les deux cas.
NB : image en gris, tous les codes sont en OpenCV-Python
une simple dilatation et érosion fermerait assez bien les brèches, j'imagine. Je pense que c'est peut-être ce que vous cherchez.
Une solution plus robuste serait de faire un bord de détecter sur l'ensemble de l'image, puis une transformation de hough pour les cercles. Un rapide Google montre qu'il ya des échantillons de code disponibles dans diverses langues pour la détection invariante de la taille des cercles à l'aide d'une transformation hough, donc espérons que vous donnera quelque chose pour continuer.
le l'avantage d'utiliser la transformation de hough est que l'algorithme vous donnera en fait une estimation de la taille et de l'emplacement de chaque cercle, de sorte que vous pouvez reconstruire une image idéale basée sur ce modèle. Il devrait également être très robuste à se chevaucher, en particulier compte tenu de la qualité de l'image d'entrée ici (c.-à-d. moins de se soucier des faux positifs, donc peut abaisser le seuil pour les résultats).
vous pourriez être à la recherche du Fillhole transformation , une application de reconstruction d'image morphologique.
cette transformation remplira les trous dans vos pièces, même si au prix de remplir également tous les trous entre les groupes de pièces adjacentes. Les solutions basées sur l'espace ou l'ouverture suggérées par les autres affiches vous donneront probablement de meilleurs résultats de reconnaissance de haut niveau.
Essayez d'utiliser cvFindContours() de la fonction. Vous pouvez l'utiliser pour trouver les composants connectés. Avec les bons paramètres, Cette fonction renvoie une liste avec les contours de chaque composant connecté.
Trouver les contours qui représentent un trou. Ensuite, utilisez cvDrawContours () pour remplir le contour choisi par la couleur du premier plan fermant ainsi les trous.
je pense que si les objets sont touchés ou bondés, il y aura des problèmes en utilisant les contours et l'ouverture de la morophologie mathématique. Au lieu de cela, la solution simple suivante est trouvée et testée. Il fonctionne très bien, et pas seulement pour ces images, mais aussi pour toutes les autres images.
voici les étapes (optimisées) comme vu dans http://blogs.mathworks.com/steve/2008/08/05/filling-small-holes /
let I
: le image d'entrée
1. filled_I = floodfill(I). // fill every hole in the image.
2. inverted_I = invert(I)`.
3. holes_I = filled_I AND inverted_I. // finds all holes
4. cc_list = connectedcomponent(holes_I) // list of all connected component in holes_I.
5. holes_I = remove(cc_list,holes_I, smallholes_threshold_size) // remove all holes from holes_I having size > smallholes_threshold_size.
6. out_I = I OR holes_I. // fill only the small holes.
En bref, l'algorithme est de trouver tous les trous, enlever le plus gros puis écrire les petits que sur l'image originale.
j'ai cherché sur internet pour trouver une fonction imfill (comme celle de Matlab) mais travaillant en C avec OpenCV. Après quelques explications, j'ai finalement trouvé une solution:
IplImage* imfill(IplImage* src)
{
CvScalar white = CV_RGB( 255, 255, 255 );
IplImage* dst = cvCreateImage( cvGetSize(src), 8, 3);
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* contour = 0;
cvFindContours(src, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
cvZero( dst );
for( ; contour != 0; contour = contour->h_next )
{
cvDrawContours( dst, contour, white, white, 0, CV_FILLED);
}
IplImage* bin_imgFilled = cvCreateImage(cvGetSize(src), 8, 1);
cvInRangeS(dst, white, white, bin_imgFilled);
return bin_imgFilled;
}
pour ceci: image binaire originale
résultat: image binaire finale
le truc est dans le paramétrage des cvDrawContours fonction: cvDrawContours (dst, contour, white, white, 0, CV_FILLED);
- dst = image de destination
- contour = pointeur vers le premier contour
- blanc = couleur utilisée pour remplir le contour
- 0 = Niveau Maximal pour les contours tracés. Si 0, seul le contour est dessiné
- CV_FILLED = Épaisseur des lignes, les contours sont dessinés avec de la. Si elle est négative (par exemple, = CV_FILLED), les intérieurs de contour sont dessinés.
plus d'informations dans la documentation openCV.
il y a probablement un moyen d'obtenir" dst " directement comme une image binaire, mais je n'ai pas pu trouver comment utiliser la fonction cvDrawContours avec des valeurs binaires.
dans le cas où quelqu'un est à la recherche de la mise en œuvre du RPC -
std::vector<std::vector<cv::Point> > contours_vector;
cv::findContours(input_image, contours_vector, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
cv::Mat contourImage(input_image.size(), CV_8UC1, cv::Scalar(0));
for ( ushort contour_index = 0; contour_index < contours_vector.size(); contour_index++) {
cv::drawContours(contourImage, contours_vector, contour_index, cv::Scalar(255), -1);
}
cv::imshow("con", contourImage);
cv::waitKey(0);