C# 图像大小调整性能:System.Drawing vs System.Windows.Media
我遇到了一个需要调整大量图像大小的情况。这些图像当前以.jpg文件的形式存储在文件系统中,但我希望稍后在项目的内存中只存储byte[]。源图像大小是可变的,但是输出应该是3个不同的预定大小。应保留纵横比,用空白填充原始图像(即,一个非常高的图像将调整大小以适应方形目标图像大小,左右两侧有大面积的白色) 我最初以.NET2.0为目标构建项目,并使用System.Drawing类执行加载/调整大小/保存。有关守则包括:C# 图像大小调整性能:System.Drawing vs System.Windows.Media,c#,wpf,performance,image,resize,C#,Wpf,Performance,Image,Resize,我遇到了一个需要调整大量图像大小的情况。这些图像当前以.jpg文件的形式存储在文件系统中,但我希望稍后在项目的内存中只存储byte[]。源图像大小是可变的,但是输出应该是3个不同的预定大小。应保留纵横比,用空白填充原始图像(即,一个非常高的图像将调整大小以适应方形目标图像大小,左右两侧有大面积的白色) 我最初以.NET2.0为目标构建项目,并使用System.Drawing类执行加载/调整大小/保存。有关守则包括: original = Image.FromFile(inputFile); //
original = Image.FromFile(inputFile); //NOTE: Reused for each of the 3 target sizes
Bitmap resized = new Bitmap(size, size);
//Draw the image to a new image of the intended size
Graphics g = Graphics.FromImage(resized);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.Clear(Color.White);
g.DrawImage(original, center - width / 2f, center - height / 2f, width, height);
g.Dispose();
//Save the new image to the output path
resized.Save(outputFile, ImageFormat.Jpeg);
BitmapImage original = new BitmapImage(); //Again, reused for each of the 3 target sizes
original.BeginInit();
original.StreamSource = new MemoryStream(imageData); //imageData is a byte[] of the data loaded from a FileStream
original.CreateOptions = BitmapCreateOptions.None;
original.CacheOption = BitmapCacheOption.Default;
original.EndInit(); //Here's where the vast majority of the time is spent
original.Freeze();
// Target Rect for the resize operation
Rect rect = new Rect(center - width / 2d, center - height / 2d, width, height);
// Create a DrawingVisual/Context to render with
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawImage(original, rect);
}
// Use RenderTargetBitmap to resize the original image
RenderTargetBitmap resizedImage = new RenderTargetBitmap(
size, size, // Resized dimensions
96, 96, // Default DPI values
PixelFormats.Default); // Default pixel format
resizedImage.Render(drawingVisual);
// Encode the image using the original format and save the modified image
SaveImageData(resizedImage, outputFile);
我想将此项目移植到.NET 3.5,因此尝试使用System.Windows.Media类执行相同的功能。我让它工作,但性能很差;每个图像的处理时间大约要长50倍。大部分时间都花在加载图像上。有关守则包括:
original = Image.FromFile(inputFile); //NOTE: Reused for each of the 3 target sizes
Bitmap resized = new Bitmap(size, size);
//Draw the image to a new image of the intended size
Graphics g = Graphics.FromImage(resized);
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
g.Clear(Color.White);
g.DrawImage(original, center - width / 2f, center - height / 2f, width, height);
g.Dispose();
//Save the new image to the output path
resized.Save(outputFile, ImageFormat.Jpeg);
BitmapImage original = new BitmapImage(); //Again, reused for each of the 3 target sizes
original.BeginInit();
original.StreamSource = new MemoryStream(imageData); //imageData is a byte[] of the data loaded from a FileStream
original.CreateOptions = BitmapCreateOptions.None;
original.CacheOption = BitmapCacheOption.Default;
original.EndInit(); //Here's where the vast majority of the time is spent
original.Freeze();
// Target Rect for the resize operation
Rect rect = new Rect(center - width / 2d, center - height / 2d, width, height);
// Create a DrawingVisual/Context to render with
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawImage(original, rect);
}
// Use RenderTargetBitmap to resize the original image
RenderTargetBitmap resizedImage = new RenderTargetBitmap(
size, size, // Resized dimensions
96, 96, // Default DPI values
PixelFormats.Default); // Default pixel format
resizedImage.Render(drawingVisual);
// Encode the image using the original format and save the modified image
SaveImageData(resizedImage, outputFile);
花这么多时间,我是不是做错了什么?我尝试过在BitmapImage上使用构造函数,它接受URI,这与性能问题相同。以前有人做过类似的事情,知道有没有更注重性能的方法?还是我只需要使用系统,还是画图?谢谢 在输入完所有这些之后,我突然想到,我可以从MS加载System.Windows.Media类的符号,并逐步完成缓慢的操作。立即找到原因,并解决。输入图像与颜色配置文件一起保存,并且它正在尝试加载每个图像的颜色配置文件(从文件系统)。在上面的代码中,通过从BitmapCreateOptions.None切换到BitmapCreateOptions.IgnoreColorProfile,它不再这样做,并且执行速度与System.Drawing一样快 希望这能帮助其他遇到这个问题的人 我认为MSDN页面上的这一点可能与此相关: System.Drawing命名空间提供对GDI+基本图形功能的访问。System.Drawing.Drawing2D、System.Drawing.Imaging和System.Drawing.Text命名空间中提供了更高级的功能。 Graphics类提供了绘制到显示设备的方法。矩形和点等类封装GDI+原语。Pen类用于绘制直线和曲线,而从抽象类Brush派生的类用于填充形状的内部 使用
System.Drawing
比使用以下方法更接近实际的基本图形功能:
定义了允许在Windows演示基础(WPF)应用程序中集成富媒体,包括绘图、文本和音频/视频内容的对象。
System.Drawing
仍然受支持,因此我坚持使用它。我在您的代码中发现了一个有趣的情况。使用以下行中的删除:
using(DrawingContext drawingContext = drawingVisual.RenderOpen())
我不知道为什么会有这样的代码加速,但你可以试试看。你似乎做得很艰难。只需设置DecodePixelHeight和DecodePixelWidth,就可以让WPF为您完成这项工作。这将导致在图像加载过程中调整大小:
BitmapImage resizedImage = new BitmapImage
{
StreamSource = new MemoryStream(imageData),
CreateOptions = BitmapCreateOptions.IgnoreColorProfile,
DecodePixelHeight = height,
DecodePixelWidth = width,
}
resizedImage.BeginInit(); // Needed only so we can call EndInit()
resizedImage.EndInit(); // This does the actual loading and resizing
imageSaveImageData(resizedImage, outputFile);
我还包括了在代码中找到的IgnoreColorProfile解决方案
更新我重新阅读了您的问题,并意识到您使用DrawingVisual的原因是您需要在图像周围添加空格以使其成为方形。DecodePixelHeight和DecodePixelWidth无法实现该目标,因此我的解决方案无法回答您的问题
我将在这里留下我的答案,以防一些只需要调整大小而不带空格的人遇到这个问题。这会加快代码的速度,因为当您这样做时,它实际上不会渲染。在调用drawingContext.Close()或drawingContext超出范围(这是用户所做的)之前,绘图上下文实际上不会将您告诉它的内容绘制到参考底图视觉。