从图像中删除周围的空白

我有一块从客户那里收到的产品图片。 每个产品图像都是一张图片,它是用白色背景拍摄的。 我想裁剪图像的所有周围部分,但只留下产品在中间。 这可能吗?

举个例子:[ http://www.5dnet.de/media/catalog/product/d/r/dress_shoes_5.jpg] [1 ]

我不希望删除所有白色像素,但我确实想要裁剪图像,以便最顶部的像素行包含一个非白色像素,最左边的垂直像素行包含一个非白色像素,底部 – 大多数水平像素行包含一个非白色像素等。

C#或VB.net中的代码将不胜感激。

我已经编写了代码来自己完成这项工作 – 让基础知识变得非常困难。

实质上,您需要扫描像素行/列以检查非白色像素并隔离产品图像的边界,然后创建仅包含该区域的新位图。

请注意,虽然Bitmap.GetPixel()方法有效,但它相对较慢。 如果处理时间很重要,则需要使用Bitmap.LockBits()将位图锁定在内存中,然后在unsafe { }块内使用一些简单的指针直接访问像素。

关于CodeProject的这篇文章提供了一些你可能会觉得有用的细节。

我发现我必须调整Dmitri的答案,以确保它适用于实际上不需要裁剪的图像(水平,垂直或两者)……

  public static Bitmap Crop(Bitmap bmp) { int w = bmp.Width; int h = bmp.Height; Func allWhiteRow = row => { for (int i = 0; i < w; ++i) if (bmp.GetPixel(i, row).R != 255) return false; return true; }; Func 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); } } 

这是我(相当冗长)的解决方案:

 public Bitmap Crop(Bitmap bmp) { int w = bmp.Width, h = bmp.Height; Func allWhiteRow = row => { for (int i = 0; i < w; ++i) if (bmp.GetPixel(i, row).R != 255) return false; return true; }; Func 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); } } 

我需要一个适用于大图像的解决方案(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 } 

这当然是可能的。 在伪代码中:

 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。

修复顶部和左侧剩余的1px空白区域

  public Bitmap Crop(Bitmap bitmap) { int w = bitmap.Width; int h = bitmap.Height; Func IsAllWhiteRow = row => { for (int i = 0; i < w; i++) { if (bitmap.GetPixel(i, row).R != 255) { return false; } } return true; }; Func 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); } } 

netpbm图形实用程序库中的pnmcrop实用程序就是这样做的。

我建议查看他们的代码,可从http://netpbm.sourceforge.net/获取

 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中其他post的代码,但是有bug ,我已经改变了一些东西 ,现在它运行良好。

该post来自http://msm2020-sc.blogspot.com/2013/07/c-crop-white-space-from-around-image.html