C# 是否自动将位图修剪为最小大小?
假设我有一个32bpp ARGB模式下的C# 是否自动将位图修剪为最小大小?,c#,image-processing,bitmap,gdi+,C#,Image Processing,Bitmap,Gdi+,假设我有一个32bpp ARGB模式下的System.Drawing.Bitmap。这是一个大的位图,但是它大部分是完全透明的像素,中间有一个相对较小的图像。 什么是一个快速算法来检测“真实”图像的边界,这样我就可以裁剪掉周围所有的透明像素 或者,在.Net中是否已经有一个函数可以用于此功能?基本思想是检查图像的每个像素,以找到图像的上、左、右和下边界。要有效地执行此操作,请不要使用GetPixel方法,因为该方法非常慢。使用锁位 以下是我提出的实施方案: static Bitmap TrimB
System.Drawing.Bitmap
。这是一个大的位图,但是它大部分是完全透明的像素,中间有一个相对较小的图像。
什么是一个快速算法来检测“真实”图像的边界,这样我就可以裁剪掉周围所有的透明像素
或者,在.Net中是否已经有一个函数可以用于此功能?基本思想是检查图像的每个像素,以找到图像的上、左、右和下边界。要有效地执行此操作,请不要使用
GetPixel
方法,因为该方法非常慢。使用锁位
以下是我提出的实施方案:
static Bitmap TrimBitmap(Bitmap source)
{
Rectangle srcRect = default(Rectangle);
BitmapData data = null;
try
{
data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] buffer = new byte[data.Height * data.Stride];
Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);
int xMin = int.MaxValue;
int xMax = 0;
int yMin = int.MaxValue;
int yMax = 0;
for (int y = 0; y < data.Height; y++)
{
for (int x = 0; x < data.Width; x++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
if (x < xMin) xMin = x;
if (x > xMax) xMax = x;
if (y < yMin) yMin = y;
if (y > yMax) yMax = y;
}
}
}
if (xMax < xMin || yMax < yMin)
{
// Image is empty...
return null;
}
srcRect = Rectangle.FromLTRB(xMin, yMin, xMax, yMax);
}
finally
{
if (data != null)
source.UnlockBits(data);
}
Bitmap dest = new Bitmap(srcRect.Width, srcRect.Height);
Rectangle destRect = new Rectangle(0, 0, srcRect.Width, srcRect.Height);
using (Graphics graphics = Graphics.FromImage(dest))
{
graphics.DrawImage(source, destRect, srcRect, GraphicsUnit.Pixel);
}
return dest;
}
静态位图修剪位图(位图源)
{
矩形srcRect=默认值(矩形);
BitmapData数据=null;
尝试
{
data=source.LockBits(新矩形(0,0,source.Width,source.Height),ImageLockMode.ReadOnly,PixelFormat.Format32bppArgb);
字节[]缓冲区=新字节[data.Height*data.Stride];
Marshal.Copy(data.Scan0,buffer,0,buffer.Length);
int xMin=int.MaxValue;
int xMax=0;
int yMin=int.MaxValue;
int-yMax=0;
对于(int y=0;yxMax)xMax=x;
如果(yyMax)yMax=y;
}
}
}
if(xMax
它可能会被优化,但我不是GDI+专家,所以这是我在没有进一步研究的情况下所能做的最好的
编辑:实际上,有一种简单的方法可以优化它,即不扫描图像的某些部分:
截断是直的吗?如果是这样,从L->R和T->B读取像素会非常快。如果是正方形,你可能会节省更多的时间,并从四个边的中心开始进行二进制搜索(至少减少像素查询)小的,嵌入的图像也可以有透明的像素吗?不幸的是,图像可以是任何形状。我看不到比O(n^2)更好的方法我认为你的第五点是错误的:可能有几个不同的区域有不透明的像素,所以在切割线上没有不透明的像素并不意味着有任何问题,但是是的:二进制搜索并不总是适用于我的图像-例如,可能有两个图像被空格隔开。顺便说一句,我只是意识到有一种更简单的方法可以裁剪图像,而无需使用图形:
返回source.Clone(srrect,source.PixelFormat);
很好的解决方案,非常有用,但我发现我的图像被剪裁了太多像素。从逻辑上看,你的方法似乎是正确的,但我将调用改为Rectangle.FromLTRB到srrect=Rectangle.FromLTRB(xMin,yMin,xMax+1,yMax+1)现在它工作得很好。谢谢你的代码!default(矩形)
也是错误的。Windows不允许小于一个像素的位图。它会尝试失败,等等。@biterblue,这没关系,因为srrect
的初始值在使用之前总是被覆盖。@thomaslevsque不,它并不总是被覆盖。例如data=source.LockBits(…);
可以引发异常,srrect=…
位于try块的最末尾。就像我拼写错误的(^^^):“如果try失败,等等。”
static Bitmap TrimBitmap(Bitmap source)
{
Rectangle srcRect = default(Rectangle);
BitmapData data = null;
try
{
data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] buffer = new byte[data.Height * data.Stride];
Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);
int xMin = int.MaxValue,
xMax = int.MinValue,
yMin = int.MaxValue,
yMax = int.MinValue;
bool foundPixel = false;
// Find xMin
for (int x = 0; x < data.Width; x++)
{
bool stop = false;
for (int y = 0; y < data.Height; y++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
xMin = x;
stop = true;
foundPixel = true;
break;
}
}
if (stop)
break;
}
// Image is empty...
if (!foundPixel)
return null;
// Find yMin
for (int y = 0; y < data.Height; y++)
{
bool stop = false;
for (int x = xMin; x < data.Width; x++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
yMin = y;
stop = true;
break;
}
}
if (stop)
break;
}
// Find xMax
for (int x = data.Width - 1; x >= xMin; x--)
{
bool stop = false;
for (int y = yMin; y < data.Height; y++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
xMax = x;
stop = true;
break;
}
}
if (stop)
break;
}
// Find yMax
for (int y = data.Height - 1; y >= yMin; y--)
{
bool stop = false;
for (int x = xMin; x <= xMax; x++)
{
byte alpha = buffer[y * data.Stride + 4 * x + 3];
if (alpha != 0)
{
yMax = y;
stop = true;
break;
}
}
if (stop)
break;
}
srcRect = Rectangle.FromLTRB(xMin, yMin, xMax, yMax);
}
finally
{
if (data != null)
source.UnlockBits(data);
}
Bitmap dest = new Bitmap(srcRect.Width, srcRect.Height);
Rectangle destRect = new Rectangle(0, 0, srcRect.Width, srcRect.Height);
using (Graphics graphics = Graphics.FromImage(dest))
{
graphics.DrawImage(source, destRect, srcRect, GraphicsUnit.Pixel);
}
return dest;
}