У меня есть карта ребер, извлеченная из модуля обнаружения границ в OpenCV (обнаружение canny edge). То, что я хочу сделать, это заполнить отверстия в карте края.
Я использую библиотеки С++ и OpenCV. В OpenCV есть функция cvFloodFill(), и она заполнит отверстия семенем (одним из мест для начала наводнения). Тем не менее, я пытаюсь заполнить все внутренние отверстия, не зная семена. (Аналогично imfill() в MATLAB)
Q1: как найти все семена, чтобы я мог применять 'cvFloodFill()'? Q2: как реализовать эквивалент 'imfill()'?
Новичок в OpenCV, и любой намек оценивается.
В соответствии с документацией imfill
в MATLAB:
BW2 = imfill(BW,'holes');
заполняет отверстия в двоичном изображении
BW
. Отверстие представляет собой набор фоновых пикселей, которые не могут быть достигнуты путем заполнения фона от края изображения.
Поэтому, чтобы получить "дырочные" пиксели, сделайте вызов cvFloodFill
с левым угловым пикселем изображения в виде семени, Вы получаете отверстия, дополняя изображение, полученное на предыдущем шаге.
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)
Функция cvDrawContours имеет возможность заполнить контуры, которые вы нарисовали.
Вот краткий пример cvDrawContours (IplImage, контуры, цвет, цвет, -1, CV_FILLED, 8);
Вот документация
Я думаю, вы опубликовали это давным-давно, но я надеюсь, что это поможет кому-то.
Это исходный код (на С#):
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);
Я просматривал интернет, чтобы найти правильную imfill функцию (как в Matlab), но работающую на С++ с OpenCV. После некоторых реасиксов я наконец придумал решение:
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;
}
Для этого: Оригинальное двоичное изображение
Результат: Конечное двоичное изображение
Фокус в настройках параметров функции cvDrawContours: cvDrawContours (dst, контур, белый, белый, 0, CV_FILLED);
Дополнительная информация в документации openCV.
Вероятно, есть способ получить "dst" непосредственно как двоичное изображение, но я не мог найти, как использовать функцию cvDrawContours с двоичными значениями.
Я сделал простую функцию, эквивалентную matlab imfill ('hole'). Я не тестировал его во многих случаях, но он работал до сих пор. Я использую его на изображениях края, но он принимает любое двоичное изображение, например, из операции порога.
Отверстие - это не более чем набор пикселей, которые не могут быть "достигнуты", когда фон заполнен, поэтому
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;
}
Вот пример результата
Здесь быстрый и грязный подход:
Просто приложение для Ответ 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;
}
}
Недавно я также нашел решение этой проблемы. Здесь я реализовал идею 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;
}
Надеюсь, это будет полезно.
Вы пробовали ContourFinding над Cannyied Image?
cvFindContours создает своеобразное дерево, в котором внешние счетчики являются родителями для внутренних контуров ( "дыры" ). См. Образец contours.py. Из контуров вы можете извлекать семена
FindContours
идентифицирует много поддельных контуров, где есть резкий поворот краев, так что вы не можете точно их пометить.
Если у вас есть точки из ребер, вы можете использовать fillConvexPoly() или fillPoly() (если поли не выпуклый).
Один из способов получить точки из ребер - сделать findContours() → approxPolyDP().
holes
вimfill
. Я хотел бы сделать это на OpenCV Python, и вы показали мне путь. Спасибо амро!