2008-10-29 6 views
21

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

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

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

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

ответ

6

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

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

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

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

+0

Несет ли это слишком много времени, чтобы обработать для 1000x1000 изображения? Пожалуйста посоветуй. – techno 2016-02-20 10:45:56

+0

Зависит от вашего определения «слишком много времени», которое зависит от вашего контекста. Я бы предложил написать код, используя «Bitmap.GetPixel()», а затем сравнив результат, чтобы увидеть. Также обратите внимание, что интеллектуальный алгоритм более важен, чем микро-оптимизация отдельных считываний пикселей. – Bevan 2016-02-24 02:10:13

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.

1

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

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

16

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

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); 
    } 
} 
+1

работает отлично, за исключением случаев, когда croppedWidth или croppedHeight равно нулю, в этом случае я устанавливаю их в bmp.Width или bmp.Height соответственно, и он работает как шарм :) – 2011-12-25 14:23:59

+0

делает это для каждого изображения? как png jpeg или gif? – MonsterMMORPG 2016-06-29 11:50:55

29

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

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); 
     } 
    } 
7

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

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 
} 
1

исправить оставшиеся 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); 
     } 
    } 
Смежные вопросы