Rellenar los agujeros en OpenCV

Tengo un mapa de bordes extraído del módulo de detección de bordes en OpenCV (detección de bordes astutos). Lo que quiero hacer es llenar los agujeros en el mapa del borde.

Estoy usando bibliotecas C ++ y OpenCV . En OpenCV hay una función cvFloodFill () , y rellenará los agujeros con una semilla (con una de las ubicaciones para comenzar a inundar). Sin embargo, estoy tratando de llenar todos los agujeros interiores sin saber las semillas. (Similar a imfill () en MATLAB)

P1: ¿cómo encontrar todas las semillas, para poder aplicar ‘cvFloodFill ()’?
P2: ¿cómo implementar un equivalente ‘imfill ()’?

Novato en OpenCV, y cualquier pista es apreciada.

De acuerdo con la documentación de imfill en MATLAB:

 BW2 = imfill(BW,'holes'); 

llena los agujeros en la imagen binaria BW . Un agujero es un conjunto de píxeles de fondo que no se puede alcanzar rellenando el fondo desde el borde de la imagen.

Por lo tanto, para obtener los “agujeros” de los píxeles, realice una llamada a cvFloodFill con el píxel de la esquina izquierda de la imagen como una semilla. Obtienes los agujeros al complementar la imagen obtenida en el paso anterior.

Ejemplo de 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) 

captura de pantalla1 captura de pantalla2

la función cvDrawContours tiene una opción para rellenar los contornos que ha trazado.

Aquí hay un pequeño ejemplo de cvDrawContours (IplImage, contornos, color, color, -1, CV_FILLED, 8);

Aquí está la documentación

http://opencv.willowgarage.com/documentation/drawing_functions.html?highlight=cvdrawcontours#cvDrawContours

Supongo que publicaste esto hace mucho tiempo, pero espero que ayude a alguien.

Este es el código fuente (en C #):

  Image image = new Image(@"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); 

Resultado

He estado buscando en Internet para encontrar una función de relleno adecuada (como la de Matlab) pero trabajando en C ++ con OpenCV. Después de algunos reaserches, finalmente se me ocurrió una solución:

 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; } 

Para esto: Imagen Binaria Original

El resultado es: Imagen Binaria Final

El truco está en la configuración de parámetros de la función cvDrawContours: cvDrawContours (dst, contour, white, white, 0, CV_FILLED);

  • dst = imagen de destino
  • contorno = puntero al primer contorno
  • blanco = color utilizado para rellenar el contorno
  • 0 = nivel máximo para contornos dibujados. Si es 0, solo se dibuja el contorno
  • CV_FILLED = Grosor de las líneas con las que se dibujan los contornos. Si es negativo (Por ejemplo, = CV_FILLED), se dibujan los interiores del contorno.

Más información en la documentación de openCV.

Probablemente haya una manera de obtener “dst” directamente como una imagen binaria, pero no pude encontrar cómo usar la función cvDrawContours con valores binarios.

Hice una función simple que es equivalente al relleno de matlab (‘agujeros’). No lo he probado en muchos casos, pero hasta ahora ha funcionado. Lo estoy usando en imágenes de borde pero acepta cualquier tipo de imagen binaria, como desde una operación de umbral.

Un agujero no es más que un conjunto de píxeles que no se puede “alcanzar” cuando se llena el fondo, entonces,

 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; } 

Aquí hay un ejemplo de resultado

enter image description here

Aquí hay un enfoque rápido y sucio:

  1. Realiza una captura en tu imagen de entrada para que la nueva imagen binaria tenga 1 en los bordes y 0 en caso contrario
  2. Encuentre el primer 0 a lo largo de un lado de su imagen de borde, e inicie un relleno con 1 en ese punto en una imagen en blanco usando su imagen de borde como máscara. (Esperamos que no hayamos tenido mala suerte y sembremos este primer relleno en el interior de una forma que está medio fuera de la pantalla)
  3. Esta nueva imagen inundada es el ‘fondo’. Cualquier píxel aquí que tenga un 1 es el fondo, y cualquier píxel que tenga un 0 es el primer plano.
  4. Pasa por la imagen y encuentra los píxeles de primer plano. Siembra una inundación en cualquier que encuentres.
  5. O esta nueva imagen inundada con su imagen de Canny del paso 1, y listo.

Solo un apéndice para la respuesta 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 

¿Has probado ContourFinding sobre Cannyied Image?

cvFindContours crea una especie de árbol en el cual los contornos exteriores son padres de los contornos internos (‘agujeros’). Ver la muestra de contours.py. Desde los contornos podrías extraer semillas

Recientemente también estoy encontrando la solución a este problema. Aquí implementé la idea de Amro de la siguiente manera:

 #include  using namespace std; #include  #include  #include  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; } 

Esperamos que esto sea útil.

Si tiene los puntos de los bordes, puede usar fillConvexPoly () o fillPoly () (si poly no es convexo).

Una forma de obtener los puntos de los bordes es hacer findContours () -> approxPolyDP () .