Remplir les trous dans OpenCV [dupliquer]
cette question a déjà une réponse ici:
- remplir des trous à l'intérieur d'un objet binaire 7 réponses
j'ai une carte de bord extraite du module de détection de bord dans OpenCV (détection de bord canny). Ce que je veux, c'est combler les trous de la carte.
j'utilise c++ , et OpenCV bibliothèques. Dans OpenCV il y a une fonction cvFloodFill () , et il va remplir les trous avec une graine (avec un de l'emplacement pour commencer à inonder). Cependant, j'essaie de combler tous les trous intérieurs sans connaître les graines.(similaire à imfill () in MATLAB)
T1: comment trouver toutes les des graines, pour que je puisse appliquer 'cvFloodFill ()'?
Q2: comment mettre en œuvre un équivalent 'imfill ()'?
Newbie dans OpenCV, et tout conseil est le bienvenue.
9 réponses
selon la documentation de imfill
dans MATLAB:
BW2 = imfill(BW,'holes');
remplit des trous dans l'image binaire
BW
. Un trou est un ensemble de pixels du fond qui ne peut être atteint par le remplissage d'arrière-plan à partir du bord de l'image.
donc pour obtenir les pixels "trous", faites un appel à cvFloodFill
avec le pixel de coin gauche de la l'image comme une graine. Vous obtenez les trous en complétant l'image obtenue à l'étape précédente.
exemple MATLAB:
BW = im2bw( imread('coins.png') );
subplot(121), imshow(BW)
% used here as if it was cvFloodFill
holes = imfill(BW, [1 1]); % [1 1] is the starting location point
BW(~holes) = 1; % fill holes
subplot(122), imshow(BW)
la fonction cvDrawContours a une option pour remplir les contours que vous avez dessinés.
voici un bref exemple cvDrawContours (IplImage, contours, color, color, -1, CV_FILLED, 8 );
Voici la documentation
je suppose que vous avez posté ceci un long il y a le temps, mais j'espère que cela aide quelqu'un.
C'est le code source (en C#):
Image<Gray, byte> image = new Image<Gray, byte>(@"D:\final.bmp");
CvInvoke.cvShowImage("image 1", image);
var contours = image.FindContours();
while (contours != null)
{
CvInvoke.cvDrawContours(image, contours, new Gray(255).MCvScalar, new Gray (255).MCvScalar, 0, -1, Emgu.CV.CvEnum.LINE_TYPE.CV_AA, new DPoint(0, 0));
contours = contours.HNext;
}
CvInvoke.cvShowImage("image 2", image);
j'ai cherché sur internet 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.
j'ai fait une fonction simple qui est équivalente à imfill('trous') de matlab. Je n'ai pas testé de nombreux cas, mais il a fonctionné jusqu'à présent. Je l'utilise sur des images de bord mais il accepte tout type d'image binaire, comme une opération de battage.
un trou n'est rien de plus qu'un ensemble de pixels qui ne peut pas être "atteint" lorsque le fond est rempli ,donc
void fillEdgeImage(cv::Mat edgesIn, cv::Mat& filledEdgesOut) const
{
cv::Mat edgesNeg = edgesIn.clone();
cv::floodFill(edgesNeg, cv::Point(0,0), CV_RGB(255,255,255));
bitwise_not(edgesNeg, edgesNeg);
filledEdgesOut = (edgesNeg | edgesIn);
return;
}
voici un exemple de résultat
Voici une approche rapide et sale:
- Effectuer futé sur votre image d'entrée de sorte que la nouvelle image binaire a 1 sur les bords, et 0 autrement
- trouvez le premier 0 le long d'un côté de votre image de bord, et initiez un débordement de 1 à ce point sur une image vierge en utilisant votre image de bord comme masque. (Nous espérons ici que nous n'avons pas eu de malchance et semé ce premier remplissage à l'intérieur d'une forme qui est à moitié hors de l'écran)
- cette nouvelle image inondée est l '"arrière-plan". N'importe quel pixel ici qui a un 1 est le fond, et n'importe quel pixel qui a un 0 est le premier plan.
- boucle à travers l'image et trouver tous les pixels de premier plan. Ensemencez un tas de fumier sur ceux que vous trouverez.
- ou cette nouvelle image inondée avec votre image rusée de l'étape 1, et c'est fini.
Juste une annexe pour les Amro de réponse.
void cvFillHoles(cv::Mat &input)
{
//assume input is uint8 B & W (0 or 1)
//this function imitates imfill(image,'hole')
cv::Mat holes=input.clone();
cv::floodFill(holes,cv::Point2i(0,0),cv::Scalar(1));
for(int i=0;i<input.rows*input.cols;i++)
{
if(holes.data[i]==0)
input.data[i]=1;
}
}
avez-vous essayé de ContourFinding au-dessus de L'Image Cannyied?
cvFindContours crée une sorte d'arbre dans lequel les comtours extérieurs sont parents aux contours intérieurs ('trous'). Voir contours.py échantillon. Des contours on pouvait extraire des graines
récemment, je suis aussi en train de trouver la solution à ce problème. Ici, j'ai mis en œuvre Amro l 'idée comme suit:
#include <iostream>
using namespace std;
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
using namespace cv;
int main()
{
IplImage *im = cvLoadImage("coin.png",CV_LOAD_IMAGE_ANYDEPTH);
IplImage *hole = cvCreateImage(cvSize(im->width,im->height),8,1);
cvShowImage("Original",im);
cvCopyImage(im,hole);
cvFloodFill(hole,cvPoint(0,0),cvScalar(255));
cvShowImage("Hole",hole);
cvSaveImage("hole.png",hole);
cvNot(hole,hole);
cvAdd(im,hole,im);
cvShowImage("FillHole",im);
cvSaveImage("fillHole.png",im);
cvWaitKey(0);
system("pause");
return 0;
}
J'espère que ce sera utile.
si vous avez les points des bords, vous pouvez utiliser fillConvexPoly() ou fillPoly () (si poly non convexe).
une façon d'obtenir les points des bords est de faire findContours() -> approxPolyDP () .