7

我正在寻找一种从 c# 托管 GDI+ 库中检测 c# 位图边缘空白的解决方案。

图片要么是透明的,要么是白色的,大多数 400 倍的图片是 8000x8000 像素,边缘有大约 2000 像素的空白。

找出边缘、x、y、高度和宽度坐标的最有效方法是什么?我尝试逐个像素地运行,但发现它非常慢。

解决方案更新—— 添加了左/右/上/下边界

图像细节中心图像的问题,现在裁剪任何透明 (0%) 或白色 (#FFFFFF) 像素。

var top = bitmap.Height;
var left = bitmap.Width;
var right = 0;
var bottom = 0;

...

var pData = pData0 + (y * data.Stride) + (x * 4);
var xyAlpha = pData[3];
var xyBlue = pData[0];
var xyGreen = pData[1];
var xyRed = pData[2];
if ((xyAlpha > 0) || (xyRed != 255 && xyGreen != 255 && xyBlue != 255)) {
    if (y < top)
        top = y;
    if (y > bottom)
        bottom = y;
    if (x < left)
        left = x;
    if (x > right)
        right = x;
}

...

var cropWidth = right - left;
var cropHeight = bottom - top;
var cropX = top;
var cropY = left;

var cacheBitmap = new Bitmap(cropWidth, cropHeight, PixelFormat.Format32bppArgb);
using (var cacheGraphics = Graphics.FromImage(cacheBitmap)) {
    cacheGraphics.DrawImage(context.Image, new Rectangle(0, 0, cropWidth, cropHeight), cropX, cropY, cropWidth, cropHeight, GraphicsUnit.Pixel);
}
4

2 回答 2

9

Bob Powell 的 GDI+ 常见问题解答是一个很棒的 GDI+ 资源!

你没有说你是如何访问图像中的像素的,所以我假设你使用了慢速 GetPixel 方法。您可以使用指针和 LockBits 以更快的方式访问像素:请参阅 Bob Powells 对 LockBits 的解释

下面的代码使用 LockBits 方法(对于 PixelFormat.Format32bppArgb),并将使用发现图像中第一个和最后一个像素的值填充起点和终点,这些像素没有参数颜色中描述的颜色。该方法还会忽略完全透明的像素,如果您想检测可见“内容”开始的图像区域,这很有用。

    Point start = Point.Empty;
    Point end = Point.Empty;
    
    int bitmapWidth = bmp.Width;
    int bitmapHeight = bmp.Height;
    
    #region find start and end point
    BitmapData data = bmp.LockBits(new Rectangle(0, 0, bitmapWidth, bitmapHeight), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    try
    {
        unsafe
        {
            byte* pData0 = (byte*)data.Scan0;
            for (int y = 0; y < bitmapHeight; y++)
            {
                for (int x = 0; x < bitmapWidth; x++)
                {
                    byte* pData = pData0 + (y * data.Stride) + (x * 4);
    
                    byte xyBlue = pData[0];
                    byte xyGreen = pData[1];
                    byte xyRed = pData[2];
                    byte xyAlpha = pData[3];
    
    
                    if (color.A != xyAlpha
                            || color.B != xyBlue
                            || color.R != xyRed
                            || color.G != xyGreen)
                    {
                        //ignore transparent pixels
                        if (xyAlpha == 0)
                            continue;
                        if (start.IsEmpty)
                        {
                            start = new Point(x, y);
                        }
                        else if (start.Y > y)
                        {
                            start.Y = y;
                        }
                        if (end.IsEmpty)
                        {
                            end = new Point(x, y);
                        }
                        else if (end.X < x)
                        {
                            end.X = x;
                        }
                        else if (end.Y < y)
                        {
                            end.Y = y;
                        }
                    }
                }
            }
        }
    }
    finally
    {
        bmp.UnlockBits(data);
    }
    #endregion
于 2009-02-01T03:11:13.190 回答
2

我首先要确保使用 Patrick 描述的 LockBits 方法。其次,我会检查中间线上的像素以快速确定边缘。中间线我的意思是,如果您说例如 2000x1000 图像,您首先会沿着水平线编号 500(1000 中)查找左右界限,然后沿着垂直线编号 1000(2000 中)找到上限和下限。这种方式应该非常快。

于 2009-02-01T03:26:55.030 回答