Как найти ограничивающие прямоугольники заданного цвета в растровом изображении? - PullRequest
1 голос
/ 13 июля 2020

У меня есть растровое изображение с различными цветовыми узорами, и мне нужно найти ограничивающие прямоугольники одного заданного цвета (например, красного) в растровом изображении. Я нашел код для обработки изображений, но не могу понять, как этого добиться.

Любая помощь будет принята с благодарностью.

Это мой код.

private void LockUnlockBitsExample(PaintEventArgs e)
{

    // Create a new bitmap.
    Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");

    // Lock the bitmap's bits.  
    Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
    System.Drawing.Imaging.BitmapData bmpData =
        bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
        bmp.PixelFormat);

    // Get the address of the first line.
    IntPtr ptr = bmpData.Scan0;

    // Declare an array to hold the bytes of the bitmap.
    int bytes  = Math.Abs(bmpData.Stride) * bmp.Height;
    byte[] rgbValues = new byte[bytes];

    // Copy the RGB values into the array.
    System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);

    // Set every third value to 255. A 24bpp bitmap will look red.  
    for (int counter = 2; counter < rgbValues.Length; counter += 3)
        rgbValues[counter] = 255;

    // Copy the RGB values back to the bitmap
    System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);

    // Unlock the bits.
    bmp.UnlockBits(bmpData);

    // Draw the modified image.
    e.Graphics.DrawImage(bmp, 0, 150);
}

Изменить: Растровое изображение содержит solid цветных форм, может появиться несколько фигур с одинаковым цветом. Мне нужно найти ограничивающий прямоугольник каждой формы.

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

Я могу предоставить x, y координаты точки на Bitmap, чтобы найти связанный прямоугольник цвета.

Ответы [ 3 ]

1 голос
/ 13 июля 2020

Если вас не беспокоит производительность и вам достаточно точного соответствия цветов, то просто отсканируйте растровое изображение:

var l = bmp.Width; var t = bmp.Height; var r = 0; var b = 0;
for (var i = 0; i<rgbValues.Length, i++)
{
    if(rgbValues[i] == 255) // rgb representation of red; 
    {
        l = Math.Min(l, i % bmpData.Stride); r = Math.Max(r, i % bmpData.Stride);
        t = Math.Min(l, i / bmpData.Stride); b = Math.Max(b, i / bmpData.Stride);
    }
}
if(l>=r) // at least one point is found
    return new Rectangle(l, t, r-l+1, b-t+1);
else
    return new Rectangle(0, 0, 0, 0); // nothing found
      
1 голос
/ 13 июля 2020

Вы бы сделали это так же, как любой другой код, где вы хотите найти минимальное или максимальное значение в списке. С той разницей, что вы хотите найти как минимум, так и максимум в измерениях X и Y. Пример:

    public static Rectangle GetBounds(this Bitmap bmp, Color color)
    {
        int minX  = int.MaxValue;
        int minY = int.MaxValue;
        int maxX = int.MinValue;
        int maxY = int.MinValue;
        for (int y = 0; y < bmp.Height; y++)
        {
            for (int x = 0; x < bmp.Width; x++)
            {
                var c = bmp.GetPixel(x, y);
                if (color == c)
                {
                    if (x < minX) minX = x;
                    if (x > maxX) maxX = x;
                    if (y < minY) minY = y;
                    if (y > maxY) maxY = y;
                }
            }
        }

        var width = maxX - minX;
        var height = maxY - minY;
        if (width <= 0 || height <= 0)
        {
            // Handle case where no color was found, or if color is a single row/column 
            return default;
        }
        return new Rectangle(minX, minY, width, height);
    }

Существует множество ресурсов о том, как использовать LockBits / указатели . Так что преобразование кода для использования этого вместо GetPixel остается в качестве упражнения.

0 голосов
/ 15 июля 2020

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

После того, как вы получите все точки для каждой, вы можете вычислить ограничивающий прямоугольник через первую и последнюю точки.

public static IEnumerable<Rectangle> GetColorRectangles(Bitmap src, Color color)
{
    var rects = new List<Rectangle>();
    var points = new List<Point>();
    var srcRec = new Rectangle(0, 0, src.Width, src.Height);
    var srcData = src.LockBits(srcRec, ImageLockMode.ReadOnly, src.PixelFormat);
    var srcBuff = new byte[srcData.Stride * srcData.Height];
    var pixSize = Image.GetPixelFormatSize(src.PixelFormat) / 8;

    Marshal.Copy(srcData.Scan0, srcBuff, 0, srcBuff.Length);
    src.UnlockBits(srcData);

    Rectangle GetColorRectangle()
    {
        var curX = points.First().X;
        var curY = points.First().Y + 1;
        var maxX = points.Max(p => p.X);

        for(var y = curY; y < src.Height; y++)
            for(var x = curX; x <= maxX; x++)
            {
                var pos = (y * srcData.Stride) + (x * pixSize);
                var blue = srcBuff[pos];
                var green = srcBuff[pos + 1];
                var red = srcBuff[pos + 2];

                if (Color.FromArgb(red, green, blue).ToArgb().Equals(color.ToArgb()))
                    points.Add(new Point(x, y));
                else
                    break;
            }

        var p1 = points.First();
        var p2 = points.Last();

        return new Rectangle(p1.X, p1.Y, p2.X - p1.X, p2.Y - p1.Y);
    }

    for (var y = 0; y < src.Height; y++)
    {
        for (var x = 0; x < src.Width; x++)
        {
            var pos = (y * srcData.Stride) + (x * pixSize);
            var blue = srcBuff[pos];
            var green = srcBuff[pos + 1];
            var red = srcBuff[pos + 2];

            if (Color.FromArgb(red, green, blue).ToArgb().Equals(color.ToArgb()))
            {
                var p = new Point(x, y);

                if (!rects.Any(r => new Rectangle(r.X - 2, r.Y - 2,
                    r.Width + 4,  r.Height + 4).Contains(p)))
                    points.Add(p);
            }
        }

        if (points.Any())
        {
            var rect = GetColorRectangle();
            rects.Add(rect);
            points.Clear();
        }
    }

    return rects;
}

Демо

private IEnumerable<Rectangle> shapesRects = Enumerable.Empty<Rectangle>();

private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
    var sx = 1f * pictureBox1.Width / pictureBox1.ClientSize.Width;
    var sy = 1f * pictureBox1.Height / pictureBox1.ClientSize.Height;
    var p = Point.Round(new PointF(e.X * sx, e.Y * sy));
    var c = (pictureBox1.Image as Bitmap).GetPixel(p.X, p.Y);

    shapesRects = GetColorRectangles(pictureBox1.Image as Bitmap, c);
    pictureBox1.Invalidate();
}

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
    if (shapesRects.Any())
        using (var pen = new Pen(Color.Black, 2))
        e.Graphics.DrawRectangles(pen, shapesRects.ToArray());
}

SOA62875861

...