c # как заставить заливку работать над цветовыми градиентами? - PullRequest
0 голосов
/ 29 января 2019

Я еще не очень разбираюсь в c # и должен написать алгоритм заливки, который также работает, когда есть небольшое изменение цвета (как тень на картинке).Я нашел четырехсторонний алгоритм на основе стека, который я изменил, чтобы также изменять пиксели, которые имеют немного другое значение в спектре RGB, чем предыдущий (вся часть "RGB-Test"), а не только область с однимцвет:

private void FloodFill2(Bitmap bmp, Point pt, Color targetColor, Color replacementColor)
    {
        Stack<Point> pixels = new Stack<Point>();
        pixels.Push(pt);

        while (pixels.Count > 0)
        {
            Point a = pixels.Pop();
            if (a.X < bmp.Width && a.X > 0 &&
                    a.Y < bmp.Height && a.Y > 0)//make sure we stay within bounds
            {
                // RGB-Test Start
                green = false;
                red = false;
                blue = false;

                if (bmp.GetPixel(a.X, a.Y).G > targetColor.G)
                {
                    if (targetColor.G - bmp.GetPixel(a.X, a.Y).G > (-20))
                    {
                        green = true;
                    }
                }
                else
                {
                    if (bmp.GetPixel(a.X, a.Y).G - targetColor.G > (-20))
                    {
                        green = true;
                    }
                }

                if (bmp.GetPixel(a.X, a.Y).R > targetColor.R)
                {
                    if (targetColor.R - bmp.GetPixel(a.X, a.Y).R > (-20))
                    {
                        red = true;
                    }
                }
                else
                {
                    if (bmp.GetPixel(a.X, a.Y).R - targetColor.R > (-20))
                    {
                        red = true;
                    }
                }

                if (bmp.GetPixel(a.X, a.Y).B > targetColor.B)
                {
                    if (targetColor.B - bmp.GetPixel(a.X, a.Y).B > (-20))
                    {
                        blue = true;
                    }
                }
                else
                {
                    if (bmp.GetPixel(a.X, a.Y).B - targetColor.B > (-20))
                    {
                        blue = true;
                    }
                }
                // RGB-Test End

                if (red == true && blue == true && green == true)
                { 
                    bmp.SetPixel(a.X, a.Y, replacementColor);
                    pixels.Push(new Point(a.X - 1, a.Y));
                    pixels.Push(new Point(a.X + 1, a.Y));
                    pixels.Push(new Point(a.X, a.Y - 1));
                    pixels.Push(new Point(a.X, a.Y + 1));
                }
            }
        }
        //refresh our main picture box
        pictureBox1.Image = bmp;
        pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
        return;
    }    

Проблема теперь в том, что он остановится, если градиент на изображении становится слишком сильным, что выглядит следующим образом: https://i.stack.imgur.com/15jhd.png

В качестве решения я подумализменения «targetColor» на новый цвет пикселя, который в данный момент изменяется, чтобы он мог «перемещаться» по градиенту и останавливаться только в том случае, если внезапно слишком велика разница в цвете.

Но здесь возникает проблема моего небольшого знания стеков и c # в целом, потому что при первой попытке изменить эту часть кода следующим образом

if (red == true && blue == true && green == true)
                { 
                    newColor = bmp.GetPixel(a.X, a.Y); // added this
                    bmp.SetPixel(a.X, a.Y, replacementColor);
                    pixels.Push(new Point(a.X - 1, a.Y));
                    pixels.Push(new Point(a.X + 1, a.Y));
                    pixels.Push(new Point(a.X, a.Y - 1));
                    pixels.Push(new Point(a.X, a.Y + 1));
                    targetColor = newColor; // and this
                }

я получаю результаты, которые выглядят так: https://i.stack.imgur.com/U52mF.png

Это странно, потому что он делает именно то, что должен, но только не везде, где должен, и только в виде полос на картинке..

Я благодарю вас за каждое решение и другие идеи о том, как заставить эту работу работать правильно.

Ответы [ 2 ]

0 голосов
/ 07 февраля 2019

Если, как и я, принятый ответ не работает для вас: попробуйте этот измененный код ниже: он очень похож на ответ @Adam maleks, за исключением того, что допуск отправляется методу (в отличие отжесткое кодирование) и Stack был разбит на два объекта, чтобы предотвратить ошибку типа.

    private Bitmap floodfill(Bitmap input, Point pt, Color target, Color replacementColor, int r_tol, int g_tol, int b_tol)
    {
        Stack<Point> pixels = new Stack<Point>();
        Stack<Color> colour = new Stack<Color>();

        pixels.Push(pt);
        colour.Push(target);

        while (pixels.Count > 0)
        {

            var current_pixels = pixels.Pop();
            var current_colour = colour.Pop();
            var a = new Point(current_pixels.X, current_pixels.Y);
            Color targetColor = current_colour;

            if (a.X < input.Width && a.X > 0 && a.Y < input.Height && a.Y > 0)
            {
                var green = Math.Abs(targetColor.G - input.GetPixel(a.X, a.Y).G) < r_tol;
                var red = Math.Abs(targetColor.R - input.GetPixel(a.X, a.Y).R) < g_tol;
                var blue = Math.Abs(targetColor.B - input.GetPixel(a.X, a.Y).B) < b_tol;

                if (red == true && blue == true && green == true)
                {
                    var old_pixels = input.GetPixel(a.X, a.Y);
                    input.SetPixel(a.X, a.Y, replacementColor);
                    pixels.Push(new Point(a.X - 1, a.Y));
                    colour.Push(old_pixels);
                    pixels.Push(new Point(a.X + 1, a.Y));
                    colour.Push(old_pixels);
                    pixels.Push(new Point(a.X, a.Y - 1));
                    colour.Push(old_pixels);
                    pixels.Push(new Point(a.X, a.Y + 1));
                    colour.Push(old_pixels);
                }
            }
        }

        return input;
    }
0 голосов
/ 30 января 2019

Мой подход к этой проблеме заключался в том, чтобы также хранить в стеке информацию о цвете, которая была проверена при добавлении точки в стек, например, мы проверили пиксель (10,15) с цветом (255,10,1), и во время этого мыДобавлен в стек пиксель (10,16) с информацией о предыдущем цвете (10,15), и во время проверки пикселя мы сравниваем его цвет с предыдущим.Изменения, которые я сделал:

1) включает информацию о цвете предыдущего пикселя в стеке: для этого я использовал конструкцию c # с именем touple:

Stack<(Point point, Color target)> pixels = new Stack<(Point, Color)>();
pixels.Push((pt, target));

2) при работе со стеком мы получаем пару пикселей/ targetColour

    var curr = pixels.Pop();
    var a = curr.point;
    Color targetColor = curr.target;

3) При добавлении точек в стек мы также включаем старый цвет пикселя:

var old = bmp.GetPixel(a.X, a.Y);
bmp.SetPixel(a.X, a.Y, replacementColor);
pixels.Push((new Point(a.X - 1, a.Y), old));
pixels.Push((new Point(a.X + 1, a.Y), old));
pixels.Push((new Point(a.X, a.Y - 1), old));
pixels.Push((new Point(a.X, a.Y + 1), old));

Код после некоторого рефакторинга:

void FloodFill2(Bitmap bmp, Point pt, Color target, Color replacementColor)
{
    Stack<(Point point, Color target)> pixels = new Stack<(Point, Color)>();
    pixels.Push((pt, target));

    while (pixels.Count > 0)
    {
        var curr = pixels.Pop();
        var a = curr.point;
        Color targetColor = curr.target;

        if (a.X < bmp.Width && a.X > 0 &&
                a.Y < bmp.Height && a.Y > 0)//make sure we stay within bounds
        {
            var tolerance = 10;
            var green = Math.Abs(targetColor.G - bmp.GetPixel(a.X, a.Y).G) < tolerance;
            var red = Math.Abs(targetColor.R - bmp.GetPixel(a.X, a.Y).R) < tolerance;
            var blue = Math.Abs(targetColor.B - bmp.GetPixel(a.X, a.Y).B) < tolerance;

            if (red == true && blue == true && green == true)
            {
                var old = bmp.GetPixel(a.X, a.Y);
                bmp.SetPixel(a.X, a.Y, replacementColor);
                pixels.Push((new Point(a.X - 1, a.Y), old));
                pixels.Push((new Point(a.X + 1, a.Y), old));
                pixels.Push((new Point(a.X, a.Y - 1), old));
                pixels.Push((new Point(a.X, a.Y + 1), old));
            }
        }
    }
    //refresh our main picture box
    pictureBox1.Image = bmp;
    pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
    return;
}

Окончательный эффект:

Final effect

...