c++ - tapar - masilla para metales casera
Rellenar los agujeros en OpenCV (9)
Esta pregunta ya tiene una respuesta aquí:
- Rellenar agujeros dentro de un objeto binario 6 respuestas
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.
¿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
Aquí hay un enfoque rápido y sucio:
- Realiza una captura en tu imagen de entrada para que la nueva imagen binaria tenga 1 en los bordes y 0 en caso contrario
- 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)
- 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.
- Pasa por la imagen y encuentra los píxeles de primer plano. Siembra una inundación en cualquier que encuentres.
- O esta nueva imagen inundada con su imagen de Canny del paso 1, y listo.
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
Recientemente también estoy encontrando la solución a este problema. Aquí implementé la idea de de la siguiente manera:
#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;
}
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 () .
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<input.rows*input.cols;i++)
{
if(holes.data[i]==0)
input.data[i]=1;
}
}
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
Supongo que publicaste esto hace mucho tiempo, pero espero que ayude a alguien.
Este es el código fuente (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);
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)