J'ai une carte Edge extraite du module de détection de Edge dans OpenCV (détection de bord aléatoire). Ce que je veux faire est de combler les trous dans la carte Edge.
J'utilise les bibliothèques C++ et OpenCV. Dans OpenCV, la fonction cvFloodFill () remplit les trous avec une graine (avec l’un des emplacements pour commencer l’inondation). Cependant, j'essaie de combler tous les trous intérieurs sans connaître les graines. (Semblable à imfill () dans MATLAB)
Q1: comment trouver toutes les graines pour pouvoir appliquer 'cvFloodFill ()'?
Q2: comment implémenter un équivalent 'imfill ()'?
Débutant dans OpenCV, et tout indice est apprécié.
Selon la documentation de imfill
in MATLAB:
BW2 = imfill(BW,'holes');
remplit les trous dans l'image binaire
BW
. Un trou est un ensemble de pixels d'arrière-plan impossible à atteindre en remplissant l'arrière-plan à partir du bord de l'image.
Par conséquent, pour obtenir les pixels "trous", appelez cvFloodFill
avec le pixel du coin gauche de l'image comme germe. Vous obtenez les trous en complétant l'image obtenue à l'étape précédente.
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 court exemple cvDrawContours (IplImage, contours, couleur, couleur, -1, CV_FILLED, 8);
Voici la documentation
Je suppose que vous avez posté cela il y a longtemps, mais j'espère que cela aidera 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);
Je suis allé sur Internet pour trouver une fonction imfill appropriée (comme celle de Matlab) mais fonctionnant en C++ avec OpenCV. Après quelques recherches, 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 cela: Image binaire originale
Le résultat est: Image binaire finale
L'astuce consiste à définir les paramètres de la fonction cvDrawContours: CvDrawContours (dst, contour, white, white, 0, CV_FILLED);
Plus d'informations dans la documentation openCV.
Il existe probablement un moyen d'obtenir "dst" directement sous forme d'image binaire, mais je n'ai pas trouvé comment utiliser la fonction cvDrawContours avec des valeurs binaires.
J'ai créé une fonction simple qui équivaut à imfill ('trous') de matlab. Je ne l'ai pas testé dans de nombreux cas, mais cela a fonctionné jusqu'à présent. Je l'utilise sur des images Edge, mais il accepte tout type d'image binaire, comme lors d'une opération de seuillage.
Un trou n'est pas plus qu'un ensemble de pixels qui ne peuvent pas être "atteints" quand le fond est rempli, alors,
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:
Juste une annexe pour la réponse de Amro.
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;
}
}
Récemment, je trouve aussi la solution à ce problème. Ici, j’ai implémenté l’idée de Amro :
#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 cela vous sera utile.
Avez-vous essayé ContourFinding sur l’image Cannyied?
cvFindContours crée une sorte d'arbre dans lequel les contours extérieurs sont les parents des contours intérieurs ("trous"). Voir échantillon contours.py. Des contours vous pouvez extraire des graines
Si vous avez les points sur les arêtes, vous pouvez utiliser fillConvexPoly () ou fillPoly () (si poly non convexe).
Une façon d'obtenir les points des arêtes est de faire findContours () -> approxPolyDP () .