我有一块从客户那里收到的产品图片。每个产品图像是一个东西的图片,它是用白色背景拍摄的。我想裁剪图像的所有周围部分,但只留下产品在中间。这可能吗?
举个例子:[http://www.5dnet.de/media/catalog/product/d/r/dress_shoes_5.jpg][1]
我不希望删除所有白色像素,但我确实想要裁剪图像,以便最顶部的像素行包含一个非白色像素,最左边的垂直像素行包含一个非白色像素,最底部的水平像素行包含一个非白色像素等。
C#或VB.net中的代码将不胜感激。
答案 0 :(得分:39)
我发现我必须调整Dmitri的答案,以确保它适用于实际上不需要裁剪的图像(水平,垂直或两者)...
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);
}
}
答案 1 :(得分:17)
这是我(相当冗长)的解决方案:
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);
}
}
答案 2 :(得分:9)
我需要一个适用于大图像的解决方案(GetPixel很慢),所以我在下面编写了扩展方法。它似乎在我的有限测试中运作良好。缺点是&#34;允许不安全代码&#34;必须在您的项目中进行检查。
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
}
答案 3 :(得分:7)
我自己编写代码来实现这一点 - 让基础知识变得不是很难。
基本上,您需要扫描像素行/列以检查非白色像素并隔离产品图像的边界,然后创建一个仅包含该区域的新位图。
请注意,虽然Bitmap.GetPixel()
方法有效,但速度相对较慢。如果处理时间很重要,则需要使用Bitmap.LockBits()
将位图锁定在内存中,然后在unsafe { }
块内使用一些简单的指针直接访问像素。
This article提供了一些您可能会觉得有用的更多细节。
答案 4 :(得分: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。
答案 5 :(得分:3)
在顶部和左侧修复剩余的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);
}
}
答案 6 :(得分:1)
pnmcrop
图形实用程序库中的netpbm
实用程序就是这样做的。
我建议查看他们的代码,可从http://netpbm.sourceforge.net/
获取答案 7 :(得分:1)
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的其他帖子中获得了代码,但是有 bug ,我已经更改了一些内容,现在可以正常使用了。
http://msm2020-sc.blogspot.com/2013/07/c-crop-white-space-from-around-image.html上的帖子