gpt4 book ai didi

c# - 自动修剪位图到最小尺寸?

转载 作者:可可西里 更新时间:2023-11-01 08:34:54 26 4
gpt4 key购买 nike

假设我有一个 32bpp ARGB 模式的 System.Drawing.Bitmap。这是一个大位图,但它主要是完全透明的像素,中间某处有一个相对较小的图像。

什么是检测“真实”图像边界的快速算法,以便我可以裁剪掉图像周围的所有透明像素?

或者,.Net 中是否已有我可以用于此目的的函数?

最佳答案

基本思想是检查图像的每个像素以找到图像的上、左、右和下边界。要有效地执行此操作,请不要使用非常慢的 GetPixel 方法。请改用 LockBits

这是我想出的实现:

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

它可能可以优化,但我不是 GDI+ 专家,所以这是我在没有进一步研究的情况下能做的最好的...


编辑:实际上,有一种简单的方法可以优化它,即不扫描图像的某些部分:

  1. 从左到右扫描,直到找到不透明的像素;将 (x, y) 存入 (xMin, yMin)
  2. 从上到下扫描直到找到不透明像素(仅适用于 x >= xMin);将 y 存入 yMin
  3. 从右到左扫描,直到找到一个不透明的像素(仅适用于 y >= yMin);将 x 存入 xMax
  4. 从下到上扫描直到找到不透明像素(仅适用于 xMin <= x <= xMax);将 y 存入 yMax

EDIT2:这是上述方法的实现:

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

如果非透明部分很小,当然不会有显着的增益,因为它仍然会扫描大部分像素。但如果它很大,则只会扫描非透明部分周围的矩形。

关于c# - 自动修剪位图到最小尺寸?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4820212/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com