Подтвердить что ты не робот

Удалите окружающие пробелы с изображения

У меня есть блок изображений продуктов, которые мы получили от клиента. Каждый образ продукта представляет собой картину чего-то, и он был взят на белом фоне. Я хотел бы обрезать все окружающие части изображения, но оставить только продукт посередине. Возможно ли это?

В качестве примера: [http://www.5dnet.de/media/catalog/product/d/r/dress_shoes_5.jpg][1]

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

Код в С# или VB.net будет оценен.

4b9b3361

Ответ 1

Я написал код, чтобы сделать это сам - это не слишком сложно, чтобы получить основы.

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

Обратите внимание, что, хотя метод Bitmap.GetPixel() работает, он относительно медленный. Если важно время обработки, вам нужно использовать Bitmap.LockBits() для блокировки растрового изображения в памяти, а затем некоторый простой указатель использовать внутри блока unsafe { } для прямого доступа к пикселям.

В этой статье о CodeProject вы найдете более подробную информацию, которую вы, вероятно, найдете полезной.

Ответ 2

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

    public static Bitmap Crop(Bitmap bmp)
    {
        int w = bmp.Width;
        int h = bmp.Height;

        Func<int, bool> allWhiteRow = row =>
        {
            for (int i = 0; i < w; ++i)
                if (bmp.GetPixel(i, row).R != 255)
                    return false;
            return true;
        };

        Func<int, bool> allWhiteColumn = col =>
        {
            for (int i = 0; i < h; ++i)
                if (bmp.GetPixel(col, i).R != 255)
                    return false;
            return true;
        };

        int topmost = 0;
        for (int row = 0; row < h; ++row)
        {
            if (allWhiteRow(row))
                topmost = row;
            else break;
        }

        int bottommost = 0;
        for (int row = h - 1; row >= 0; --row)
        {
            if (allWhiteRow(row))
                bottommost = row;
            else break;
        }

        int leftmost = 0, rightmost = 0;
        for (int col = 0; col < w; ++col)
        {
            if (allWhiteColumn(col))
                leftmost = col;
            else
                break;
        }

        for (int col = w - 1; col >= 0; --col)
        {
            if (allWhiteColumn(col))
                rightmost = col;
            else
                break;
        }

        if (rightmost == 0) rightmost = w; // As reached left
        if (bottommost == 0) bottommost = h; // As reached top.

        int croppedWidth = rightmost - leftmost;
        int croppedHeight = bottommost - topmost;

        if (croppedWidth == 0) // No border on left or right
        {
            leftmost = 0;
            croppedWidth = w;
        }

        if (croppedHeight == 0) // No border on top or bottom
        {
            topmost = 0;
            croppedHeight = h;
        }

        try
        {
            var target = new Bitmap(croppedWidth, croppedHeight);
            using (Graphics g = Graphics.FromImage(target))
            {
                g.DrawImage(bmp,
                  new RectangleF(0, 0, croppedWidth, croppedHeight),
                  new RectangleF(leftmost, topmost, croppedWidth, croppedHeight),
                  GraphicsUnit.Pixel);
            }
            return target;
        }
        catch (Exception ex)
        {
            throw new Exception(
              string.Format("Values are topmost={0} btm={1} left={2} right={3} croppedWidth={4} croppedHeight={5}", topmost, bottommost, leftmost, rightmost, croppedWidth, croppedHeight),
              ex);
        }
    }

Ответ 3

Здесь мое (довольно длинное) решение:

public Bitmap Crop(Bitmap bmp)
{
  int w = bmp.Width, h = bmp.Height;

  Func<int, bool> allWhiteRow = row =>
  {
    for (int i = 0; i < w; ++i)
      if (bmp.GetPixel(i, row).R != 255)
        return false;
    return true;
  };

  Func<int, bool> allWhiteColumn = col =>
  {
    for (int i = 0; i < h; ++i)
      if (bmp.GetPixel(col, i).R != 255)
        return false;
    return true;
  };

  int topmost = 0;
  for (int row = 0; row < h; ++row)
  {
    if (allWhiteRow(row))
      topmost = row;
    else break;
  }

  int bottommost = 0;
  for (int row = h - 1; row >= 0; --row)
  {
    if (allWhiteRow(row))
      bottommost = row;
    else break;
  }

  int leftmost = 0, rightmost = 0;
  for (int col = 0; col < w; ++col)
  {
    if (allWhiteColumn(col))
      leftmost = col;
    else
      break;
  }

  for (int col = w-1; col >= 0; --col)
  {
    if (allWhiteColumn(col))
      rightmost = col;
    else
      break;
  }

  int croppedWidth = rightmost - leftmost;
  int croppedHeight = bottommost - topmost;
  try
  {
    Bitmap target = new Bitmap(croppedWidth, croppedHeight);
    using (Graphics g = Graphics.FromImage(target))
    {
      g.DrawImage(bmp,
        new RectangleF(0, 0, croppedWidth, croppedHeight),
        new RectangleF(leftmost, topmost, croppedWidth, croppedHeight),
        GraphicsUnit.Pixel);
    }
    return target;
  }
  catch (Exception ex)
  {
    throw new Exception(
      string.Format("Values are topmost={0} btm={1} left={2} right={3}", topmost, bottommost, leftmost, rightmost),
      ex);
  }
}

Ответ 4

Мне нужно решение, которое работало на больших изображениях (GetPixel медленный), поэтому я написал метод расширения ниже. Кажется, это хорошо работает в моем ограниченном тестировании. Недостатком является то, что "Разрешить небезопасный код" необходимо проверить в вашем проекте.

public static Image AutoCrop(this Bitmap bmp)
{
    if (Image.GetPixelFormatSize(bmp.PixelFormat) != 32)
        throw new InvalidOperationException("Autocrop currently only supports 32 bits per pixel images.");

    // Initialize variables
    var cropColor = Color.White;

    var bottom = 0;
    var left = bmp.Width; // Set the left crop point to the width so that the logic below will set the left value to the first non crop color pixel it comes across.
    var right = 0;
    var top = bmp.Height; // Set the top crop point to the height so that the logic below will set the top value to the first non crop color pixel it comes across.

    var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);

    unsafe
    {
        var dataPtr = (byte*)bmpData.Scan0;

        for (var y = 0; y < bmp.Height; y++)
        {
            for (var x = 0; x < bmp.Width; x++)
            {
                var rgbPtr = dataPtr + (x * 4);

                var b = rgbPtr[0];
                var g = rgbPtr[1];
                var r = rgbPtr[2];
                var a = rgbPtr[3];

                // If any of the pixel RGBA values don't match and the crop color is not transparent, or if the crop color is transparent and the pixel A value is not transparent
                if ((cropColor.A > 0 && (b != cropColor.B || g != cropColor.G || r != cropColor.R || a != cropColor.A)) || (cropColor.A == 0 && a != 0))
                {
                    if (x < left)
                        left = x;

                    if (x >= right)
                        right = x + 1;

                    if (y < top)
                        top = y;

                    if (y >= bottom)
                        bottom = y + 1;
                }
            }

            dataPtr += bmpData.Stride;
        }
    }

    bmp.UnlockBits(bmpData);

    if (left < right && top < bottom)
        return bmp.Clone(new Rectangle(left, top, right - left, bottom - top), bmp.PixelFormat);

    return null; // Entire image should be cropped, so just return null
}

Ответ 5

Это, безусловно, возможно. В псевдокоде:

topmost = 0
for row from 0 to numRows:
    if allWhiteRow(row): 
        topmost = row
    else:
        # found first non-white row from top
        break

botmost = 0
for row from numRows-1 to 0:
    if allWhiteRow(row): 
        botmost = row
    else:
        # found first non-white row from bottom
        break

И аналогично для левого и правого.

Код для allWhiteRow будет включать в себя просмотр пикселей в этой строке и убедитесь, что они все близко к 255 255 255.

Ответ 6

Утилита pnmcrop из библиотеки графических утилит netpbm выполняет именно это.

Я предлагаю посмотреть их код, доступный из http://netpbm.sourceforge.net/

Ответ 7

исправить оставшееся 1px белое пространство сверху и слева

    public Bitmap Crop(Bitmap bitmap)
    {
        int w = bitmap.Width;
        int h = bitmap.Height;

        Func<int, bool> IsAllWhiteRow = row =>
        {
            for (int i = 0; i < w; i++)
            {
                if (bitmap.GetPixel(i, row).R != 255)
                {
                    return false;
                }
            }
            return true;
        };

        Func<int, bool> IsAllWhiteColumn = col =>
        {
            for (int i = 0; i < h; i++)
            {
                if (bitmap.GetPixel(col, i).R != 255)
                {
                    return false;
                }
            }
            return true;
        };

        int leftMost = 0;
        for (int col = 0; col < w; col++)
        {
            if (IsAllWhiteColumn(col)) leftMost = col + 1;
            else break;
        }

        int rightMost = w - 1;
        for (int col = rightMost; col > 0; col--)
        {
            if (IsAllWhiteColumn(col)) rightMost = col - 1;
            else break;
        }

        int topMost = 0;
        for (int row = 0; row < h; row++)
        {
            if (IsAllWhiteRow(row)) topMost = row + 1;
            else break;
        }

        int bottomMost = h - 1;
        for (int row = bottomMost; row > 0; row--)
        {
            if (IsAllWhiteRow(row)) bottomMost = row - 1;
            else break;
        }

        if (rightMost == 0 && bottomMost == 0 && leftMost == w && topMost == h)
        {
            return bitmap;
        }

        int croppedWidth = rightMost - leftMost + 1;
        int croppedHeight = bottomMost - topMost + 1;

        try
        {
            Bitmap target = new Bitmap(croppedWidth, croppedHeight);
            using (Graphics g = Graphics.FromImage(target))
            {
                g.DrawImage(bitmap,
                    new RectangleF(0, 0, croppedWidth, croppedHeight),
                    new RectangleF(leftMost, topMost, croppedWidth, croppedHeight),
                    GraphicsUnit.Pixel);
            }
            return target;
        }
        catch (Exception ex)
        {
            throw new Exception(string.Format("Values are top={0} bottom={1} left={2} right={3}", topMost, bottomMost, leftMost, rightMost), ex);
        }
    }