Заполните отверстия в emgu cv

Как я могу заполнить отверстия в двоичном изображении в emgu cv ? В Aforge.net это просто, используйте класс Fillholes.

спасибо

3 ответов


Да, есть метод, но он немного грязный, поскольку он основан на операции cvFloodFill. Теперь весь этот алгоритм предназначен для заполнения области цветом, пока она не достигнет края, подобного алгоритму роста области. Чтобы использовать это эффективно, вам нужно использовать небольшое изобретательное кодирование, но я предупреждаю вас, что этот код только для того, чтобы вы начали его может потребоваться перефакторинг, чтобы ускорить процесс . В настоящее время цикл проходит через каждый из ваших пикселей, которые меньше 255, применяет проверки cvFloodFill какой размер области, а затем, если он находится под определенной областью, заполните его.

важно отметить, что копия изображения сделана из исходного изображения, которое будет поставляться в операцию cvFloodFill в качестве указателя. Если прямое изображение поставляется, то вы в конечном итоге с белым изображением.

OpenFileDialog OpenFile = new OpenFileDialog();

if (OpenFileDialog.ShowDialog() == DialogResult.OK)
{
    Image<Bgr, byte> image = new Image<Bgr, byte>(OpenFile.FileName);

            for (int i = 0; i < image.Width; i++)
            {
                for (int j = 0; j < image.Height; j++)
                {
                    if (image.Data[j, i, 0] != 255)
                    {
                        Image<Bgr, byte> image_copy = image.Copy();
                        Image<Gray, byte> mask = new Image<Gray, byte>(image.Width + 2, image.Height + 2);
                        MCvConnectedComp comp = new MCvConnectedComp();
                        Point point1 = new Point(i, j);
                        //CvInvoke.cvFloodFill(
                        CvInvoke.cvFloodFill(image_copy.Ptr, point1, new MCvScalar(255, 255, 255, 255),
                        new MCvScalar(0, 0, 0),
                        new MCvScalar(0, 0, 0), out comp,
                        Emgu.CV.CvEnum.CONNECTIVITY.EIGHT_CONNECTED,
                        Emgu.CV.CvEnum.FLOODFILL_FLAG.DEFAULT, mask.Ptr);
                        if (comp.area < 10000)
                        {
                            image = image_copy.Copy();
                        }
                    }
                }
            }
}

"новый MCvScalar(0, 0, 0), новый MCvScalar (0, 0, 0)" не очень важны в этом случае, поскольку вы только заполняете результаты двоичного изображения. Ты мог бы поиграть. с другими настройками, чтобы увидеть, каких результатов можно добиться. "if (comp.площадью ключ константа для изменения - вы хотите изменить размер отверстия, которое заполнит метод.

это те результаты, которые вы можете ожидать:

Оригинал

Original Image

результаты

The Resultant Image

проблема с этим методом заключается в том, что он чрезвычайно интенсивен в памяти, и ему удалось съесть 6GB ОЗУ на изображении 200x200, и когда я попробовал 200x300, он съел все 8GB моей ОЗУ и привел все к сбою. Если большая часть вашего изображения не белая, и вы хотите заполнить крошечные пробелы или вы можете минимизировать, где вы применяете метод, я бы его избегал. Я бы предложил написать собственный класс, чтобы изучить каждый пиксель, который не 255, и добавить количество пикселей, окружающих его. Затем вы можете записать положение каждого пикселя, которое не было 255 (в простом списке), и если ваш счетчик был ниже порог установите эти позиции на 255 в Ваших изображениях (путем итерации по списку).

Я бы придерживался класса FillHoles Aforge, если вы не хотите писать свой собственный, поскольку он предназначен для этой цели.

Ура

Крис


думал, что вопрос немного старый, я хотел бы внести альтернативное решение проблемы.

вы можете получить тот же результат, что и Крис " без проблем с памятью, если вы используете следующее:

private Image<Gray,byte> FillHoles(Image<Gray,byte> image)
    {
        var resultImage = image.CopyBlank();
        Gray gray = new Gray(255);
        using (var mem = new MemStorage())
        {
            for (var contour = image.FindContours(
                CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, 
                RETR_TYPE.CV_RETR_CCOMP, 
                mem); contour!= null; contour = contour.HNext)
            {
                resultImage.Draw(contour, gray, -1);
            }
        }

        return resultImage;
    }

хорошая вещь о методе выше заключается в том, что вы можете выборочно заполнять отверстия, которые соответствуют вашим критериям. Например, вы можете заполнить отверстия, количество пикселей которых (количество черных пикселей внутри blob) ниже 50 и т. д.

private Image<Gray,byte> FillHoles(Image<Gray,byte> image, int minArea, int maxArea)
    {
        var resultImage = image.CopyBlank();
        Gray gray = new Gray(255);

        using (var mem = new MemStorage())
        {
            for (var contour = image.FindContours(
                CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, 
                RETR_TYPE.CV_RETR_CCOMP, 
                mem); contour!= null; contour = contour.HNext)
            {
                if ( (contour.Area < maxArea) && (contour.Area > minArea) )
                    resultImage.Draw(contour, gray, -1);
            }
        }

        return resultImage;
    }

можно использовать FillConvexPoly

image.FillConvexPoly(externalContours.ToArray(), new Gray(255));