Удалить окружающие пробелы из изображения - PullRequest
25 голосов
/ 29 октября 2008

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

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

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

Код в C # или VB.net будет приветствоваться.

Ответы [ 8 ]

39 голосов
/ 01 мая 2012

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

    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);
        }
    }
17 голосов
/ 26 апреля 2009

Вот мое (довольно длинное) решение:

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);
  }
}
9 голосов
/ 22 апреля 2015

Мне нужно решение, которое работает с большими изображениями (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
}
7 голосов
/ 29 октября 2008

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

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

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

В этой статье о CodeProject содержится еще несколько деталей, которые вы, вероятно, найдете полезными.

5 голосов
/ 29 октября 2008

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

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.

3 голосов
/ 15 марта 2016

исправить оставшиеся 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);
        }
    }
1 голос
/ 25 сентября 2018
public void TrimImage() {
    int threshhold = 250;


    int topOffset = 0;
    int bottomOffset = 0;
    int leftOffset = 0;
    int rightOffset = 0;
    Bitmap img = new Bitmap(@"e:\Temp\Trim_Blank_Image.png");


    bool foundColor = false;
    // Get left bounds to crop
    for (int x = 1; x < img.Width && foundColor == false; x++)
    {
        for (int y = 1; y < img.Height && foundColor == false; y++)
        {
            Color color = img.GetPixel(x, y);
            if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
                foundColor = true;
        }
        leftOffset += 1;
    }


    foundColor = false;
    // Get top bounds to crop
    for (int y = 1; y < img.Height && foundColor == false; y++)
    {
        for (int x = 1; x < img.Width && foundColor == false; x++)
        {
            Color color = img.GetPixel(x, y);
            if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
                foundColor = true;
        }
        topOffset += 1;
    }


    foundColor = false;
    // Get right bounds to crop
    for (int x = img.Width - 1; x >= 1 && foundColor == false; x--)
    {
        for (int y = 1; y < img.Height && foundColor == false; y++)
        {
            Color color = img.GetPixel(x, y);
            if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
                foundColor = true;
        }
        rightOffset += 1;
    }


    foundColor = false;
    // Get bottom bounds to crop
    for (int y = img.Height - 1; y >= 1 && foundColor == false; y--)
    {
        for (int x = 1; x < img.Width && foundColor == false; x++)
        {
            Color color = img.GetPixel(x, y);
            if (color.R < threshhold || color.G < threshhold || color.B < threshhold)
                foundColor = true;
        }
        bottomOffset += 1;
    }


    // Create a new image set to the size of the original minus the white space
    //Bitmap newImg = new Bitmap(img.Width - leftOffset - rightOffset, img.Height - topOffset - bottomOffset);

    Bitmap croppedBitmap = new Bitmap(img);
    croppedBitmap = croppedBitmap.Clone(
                    new Rectangle(leftOffset - 3, topOffset - 3, img.Width - leftOffset - rightOffset + 6, img.Height - topOffset - bottomOffset + 6),
                    System.Drawing.Imaging.PixelFormat.DontCare);


    // Get a graphics object for the new bitmap, and draw the original bitmap onto it, offsetting it do remove the whitespace
    //Graphics g = Graphics.FromImage(croppedBitmap);
    //g.DrawImage(img, 1 - leftOffset, 1 - rightOffset);
    croppedBitmap.Save(@"e:\Temp\Trim_Blank_Image-crop.png", ImageFormat.Png);
}

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

Пост от http://msm2020 -sc.blogspot.com / 2013/07 / c-crop-white-space-from-around-image.html

1 голос
/ 30 октября 2008

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

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

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