假设我在32bpp的ARGB模式有一个 System.Drawing.Bitmap 。这是一个大的位图,但是它在中间某处的大多完全透明的像素具有相对小的图像。
Suppose I have a System.Drawing.Bitmap in 32bpp ARGB mode. It's a large bitmap, but it's mostly fully transparent pixels with a relatively small image somewhere in the middle.
什么是快速算法来检测真实形象的边界,这样我就可以从它周围的裁剪了所有的透明像素?
What is a fast algorithm to detect the borders of the "real" image, so I can crop away all the transparent pixels from around it?
另外,有已经在中的函数,我可以用这个?
Alternatively, is there a function already in .Net that I can use for this?
推荐答案的基本思想是对检查图像的每一个像素,找到顶,左,右和图象的底部边界。为了有效地做到这一点,不要使用 GetPixel 方法,这是pretty缓慢。使用 LockBits 代替。
The basic idea is to check every pixel of the image to find the top, left, right and bottom bounds of the image. To do this efficiently, don't use the GetPixel method, which is pretty slow. Use LockBits instead.
这是我想出了实现:
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 +专家,所以这是最好的,我可以不用做进一步的研究...
It can probably be optimized, but I'm not a GDI+ expert, so it's the best I can do without further research...
编辑:实际上,有一个简单的方法来优化它,通过不扫描图像的某些部分:
actually, there's a simple way to optimize it, by not scanning some parts of the image :
EDIT2:这里的方法的实现上面:
here's an implementation of the approach above:
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; }有将不会是一个显著增益如果非透明部分是小当然,因为它仍然会扫描大部分像素。但是,如果它的大,大约只有非透明部分的矩形将被扫描。
There won't be a significant gain if the non-transparent part is small of course, since it will still scan most of the pixels. But if it's big, only the rectangles around the non-transparent part will be scanned.
更多推荐
自动裁剪位图来最小尺寸?
发布评论