如何使用c#裁剪图像?
当前回答
对于任何愿意使用“不安全”代码的人来说,您可以获得比标准System.Drawing.Graphics方法更好的性能,如果使用Bitmap.Clone()则更好。
请记住,32bpp是该方法支持的唯一格式。(其他格式可以工作,只要1像素存储为4字节)
我包含了两个版本,一个使用Span,它在裁剪到较小的图像时性能稍好。 如果裁剪到1000x1000的图像,它们的速度差不多。
如果感兴趣,基准在下面。
public static class BitmapExtension
{
unsafe public static Bitmap Crop(this Bitmap bitmap, int left, int top, int width, int height)
{
Bitmap cropped = new Bitmap(width, height);
BitmapData originalData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
BitmapData croppedData = cropped.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
int* srcPixel = (int*)originalData.Scan0 + (left + originalData.Width * top);
int nextLine = originalData.Width - width;
for (int y = 0, i = 0; y < height; y++, srcPixel += nextLine)
{
for (int x = 0; x < width; x++, i++, srcPixel++)
{
*((int*)croppedData.Scan0 + i) = *srcPixel;
}
}
bitmap.UnlockBits(originalData);
cropped.UnlockBits(croppedData);
return cropped;
}
unsafe public static Bitmap CropSmall(this Bitmap bitmap, int left, int top, int width, int height)
{
Bitmap cropped = new Bitmap(width, height);
BitmapData originalData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
BitmapData croppedData = cropped.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
Span<int> srcPixels = new Span<int>((void*)originalData.Scan0, originalData.Width * originalData.Height);
int nextLine = originalData.Width - width;
for (int y = 0, i = 0, s = left + originalData.Width * top; y < height; y++, s += nextLine)
{
for (int x = 0; x < width; x++, i++, s++)
{
*((int*)croppedData.Scan0 + i) = srcPixels[s];
}
}
bitmap.UnlockBits(originalData);
cropped.UnlockBits(croppedData);
return cropped;
}
}
裁剪3440x1440到1000x1000
Method | Ns |
---|---|
My Method | 1108 |
My Method(Span) | 1141 |
Graphics | 9975 |
Clone() | 21514 |
裁剪3440x1440到256x256
Method | Ns |
---|---|
My Method | 131 |
My Method(Span) | 95 |
Graphics | 1289 |
Clone() | 19680 |
裁剪3440x1440到1440x1440
Method | Ns |
---|---|
My Method | 2237 |
My Method(Span) | 2592 |
Graphics | 9999 |
Clone() | 25925 |
其他回答
查看这个链接:http://www.switchonthecode.com/tutorials/csharp-tutorial-image-editing-saving-cropping-and-resizing
private static Image cropImage(Image img, Rectangle cropArea)
{
Bitmap bmpImage = new Bitmap(img);
return bmpImage.Clone(cropArea, bmpImage.PixelFormat);
}
只有这个示例工作没有问题:
var crop = new Rectangle(0, y, bitmap.Width, h);
var bmp = new Bitmap(bitmap.Width, h);
var tempfile = Application.StartupPath+"\\"+"TEMP"+"\\"+Path.GetRandomFileName();
using (var gr = Graphics.FromImage(bmp))
{
try
{
var dest = new Rectangle(0, 0, bitmap.Width, h);
gr.DrawImage(image,dest , crop, GraphicsUnit.Point);
bmp.Save(tempfile,ImageFormat.Jpeg);
bmp.Dispose();
}
catch (Exception)
{
}
}
使用bmp.SetResolution(形象。HorizontalResolution, image .VerticalResolution);
这可能是必要的,即使你在这里实现了最佳答案 特别是如果你的图像真的很好,分辨率不是96.0
我的测试示例:
static Bitmap LoadImage()
{
return (Bitmap)Bitmap.FromFile( @"e:\Tests\d_bigImage.bmp" ); // here is large image 9222x9222 pixels and 95.96 dpi resolutions
}
static void TestBigImagePartDrawing()
{
using( var absentRectangleImage = LoadImage() )
{
using( var currentTile = new Bitmap( 256, 256 ) )
{
currentTile.SetResolution(absentRectangleImage.HorizontalResolution, absentRectangleImage.VerticalResolution);
using( var currentTileGraphics = Graphics.FromImage( currentTile ) )
{
currentTileGraphics.Clear( Color.Black );
var absentRectangleArea = new Rectangle( 3, 8963, 256, 256 );
currentTileGraphics.DrawImage( absentRectangleImage, 0, 0, absentRectangleArea, GraphicsUnit.Pixel );
}
currentTile.Save(@"e:\Tests\Tile.bmp");
}
}
}
你可以使用[图形。使用DrawImage][1]将裁剪图像从位图绘制到图形对象上。
Rectangle cropRect = new Rectangle(...);
using (Bitmap src = Image.FromFile("") as Bitmap)
{
using (Bitmap target = new Bitmap(cropRect.Width, cropRect.Height))
{
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(src, new Rectangle(0, 0, target.Width, target.Height),
cropRect,
GraphicsUnit.Pixel);
}
}
}
比公认的答案更简单的是:
public static Bitmap cropAtRect(this Bitmap b, Rectangle r)
{
using (var nb = new Bitmap(r.Width, r.Height))
{
using (Graphics g = Graphics.FromImage(nb))
{
g.DrawImage(b, -r.X, -r.Y);
return nb;
}
}
}
并且它避免了最简单答案的“内存不足”异常风险。
注意,位图和图形是可分割的,因此使用了using子句。
编辑:我发现这是很好的png保存的位图。Save或Paint.exe,但失败的png保存,如Paint Shop Pro 6 -内容被取代。添加GraphicsUnit。Pixel给出了不同的错误结果。也许只是这些失败的png有问题。
推荐文章
- 如何在没有任何错误或警告的情况下找到构建失败的原因
- 跨线程操作无效:控件“textBox1”从创建它的线程以外的线程访问
- 否ConcurrentList<T>在。net 4.0?
- 在c#中解析字符串为日期时间
- 由Jon Skeet撰写的《Singleton》澄清
- 自定义数字格式字符串始终显示符号
- 单元测试无效方法?
- Post参数始终为空
- 使用Moq验证方法调用
- string.ToLower()和string.ToLowerInvariant()
- 如何为一个或枚举删除一个项目?
- 检查instance是否属于某个类型
- 为什么c#不提供c++风格的'friend'关键字?
- 将JavaScript引擎嵌入到。net中
- ASP。NET Core返回带有状态码的JSON