图像大小调整性能:System.Drawing vs System.Windows.Media

时间:2009-11-16 23:06:10

标签: c# wpf performance image resize

我遇到了需要调整大量图像大小的情况。这些图像当前存储在文件系统上的.jpg文件中,但我希望稍后在项目中将byte []存储在内存中。源图像大小是可变的,但输出应该是3种不同的预定大小。应该保留宽高比,用白色空间填充原始图像(即,将调整非常高的图像以适应方形目标图像大小,左侧和右侧有大面积的白色)。

我最初构建了面向.NET 2.0的项目,并使用System.Drawing类来执行加载/调整大小/保存。相关代码包括:

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

我想将此项目移植到.NET 3.5,因此尝试使用System.Windows.Media类来执行相同的功能。我得到了它,但性能很糟糕;每张图像的处理时间约为50倍。绝大部分时间都花在加载图像上。相关代码包括:

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,那里存在相同的性能问题。任何人之前做过这样的事情,知道是否有更具表现力的方式来做到这一点?或者我只是需要使用System.Drawing?谢谢!

4 个答案:

答案 0 :(得分:10)

在输入所有内容之后,我发现我可以从MS加载System.Windows.Media类的符号,并逐步完成它的速度。马上找到了原因和解决方案。输入图像使用颜色配置文件保存,并且尝试加载每个图像的颜色配置文件(来自文件系统)。通过在上面的代码中从BitmapCreateOptions.None切换到BitmapCreateOptions.IgnoreColorProfile,它不再这样做,并且执行速度与System.Drawing一样快。

希望这有助于遇到此问题的其他任何人!

答案 1 :(得分:5)

你似乎是在努力做到这一点。您可以通过设置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无法实现这一目标,因此我的解决方案无法回答您的问题。

我会在这里留下我的答案,万一有人只需要一个没有空格的大小调整就会遇到这个问题。

答案 2 :(得分:0)

我认为来自MSDN上的System.Drawing页面可能是相关的:

  

System.Drawing命名空间提供对GDI +基本图形功能的访问。 System.Drawing.Drawing2D,System.Drawing.Imaging和System.Drawing.Text命名空间中提供了更高级的功能。   Graphics类提供了绘制到显示设备的方法。 Rectangle和Point等类封装了GDI +原语。 Pen类用于绘制线条和曲线,而从抽象类Brush派生的类用于填充形状的内部。

使用System.Drawing,您比通过System.Windows.Media更接近实际的基本图形功能:

  

定义支持在Windows Presentation Foundation(WPF)应用程序中集成富媒体(包括绘图,文本和音频/视频内容)的对象。

System.Drawing仍受支持,所以我坚持这一点。

答案 3 :(得分:0)

我在你的代码中发现了一个有趣的情况。 从以下行中删除 using

using(DrawingContext drawingContext = drawingVisual.RenderOpen())

我不确定为什么这会加速代码,但你可以尝试一下。