图像调整大小性能:System.Drawing 与 System.Windows.Media
我遇到了需要调整大量图像大小的情况。这些图像目前在文件系统上存储为 .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 吗?谢谢!
I've got a situation where I need to resize a large number of images. These images are stored as .jpg files on the file system currently, but I expect to just have byte[] in memory later on in the project. The source image size is variable, but the output should be 3 different predetermined sizes. Aspect ratios should be preserved, padding the original image with white space (ie, a really tall image would be resized to fit within the square target image size, with large areas of white on the left and right).
I initially built the project targeting .NET 2.0, and using System.Drawing classes to perform the load/resize/save. Relevant code includes:
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);
I wanted to port this project to .NET 3.5, so tried using the System.Windows.Media classes to perform the same function. I got it working, however performance is terrible; processing time per image is about 50x longer. The vast majority of the time is spent loading the image. Relevant code includes:
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);
Am I doing something wrong here, to take so much time? I've tried just using the constructor on BitmapImage that takes a URI, same performance issue there. Anyone done anything like this before, know if there's a more performance-minded way to do this? Or am I just going to need to use System.Drawing still? Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
输入完所有内容后,我突然想到可以从 MS 加载 System.Windows.Media 类的符号,并单步执行速度较慢的地方。立即找到了原因,并解决了。输入图像使用颜色配置文件保存,并且它尝试加载每个图像的颜色配置文件(从文件系统)。通过在上面的代码中从 BitmapCreateOptions.None 切换到 BitmapCreateOptions.IgnoreColorProfile,它不再这样做,并且执行速度与 System.Drawing 一样快。
希望这可以帮助遇到此问题的其他人!
And after typing all that up, it occurred to me that I could load the symbols from MS for the System.Windows.Media classes, and step through where it was slow. Immediately found the cause, and the solution. The input images were saved with a color profile, and it was attempting to load that color profile (from the file system) of each image. By switching from BitmapCreateOptions.None to BitmapCreateOptions.IgnoreColorProfile in the code above, it no longer does that, and performs just as fast as System.Drawing did.
Hope this helps anyone else that runs into this problem!
看来你做这件事很困难。您只需设置 DecodePixelHeight 和 DecodePixelWidth 即可让 WPF 为您完成工作。这将导致在图像加载期间发生调整大小:
我还包含了您在代码中找到的 IgnoreColorProfile 解决方案。
更新 我重新阅读了您的问题,并意识到您使用 DrawingVisual 的原因是您需要在图像周围留出空白以使其成为正方形。 DecodePixelHeight 和 DecodePixelWidth 无法实现该目标,因此我的解决方案无法回答您的问题。
我将在这里留下我的答案,以防只需要调整大小而没有空格的人遇到这个问题。
You appear to be doing this the hard way. You can let WPF do the work for you by just setting DecodePixelHeight and DecodePixelWidth. This will cause the resize to happen during the image load:
I also included the IgnoreColorProfile solution you found in my code.
Update I reread your question and realized the reason you're using DrawingVisual is that you need whitespace around your image to make it square. DecodePixelHeight and DecodePixelWidth would not accomplish that goal, so my solution does not answer your question.
I will leave my answer here in case someone who just needs a resize without whitespace comes across this question.
我认为这来自
System.Drawing MSDN 上的
页面可能相关:通过使用 System.Drawing ,您比通过
System.Windows.Media
其中:System.Drawing
仍然受支持,所以我会坚持下去。I think this from the
System.Drawing
page on MSDN might be relevant:By using
System.Drawing
you are closer to the actual basic graphics functionality than if you go viaSystem.Windows.Media
which:System.Drawing
is still supported, so I'd stick with that.我在你的代码中发现了一个有趣的情况。 从以下行中删除
using
:我不确定为什么这会加速代码,但您可以尝试一下。
I found an interesting situation in your code. Remove
using
from following line:I'm not sure why this speed up code, but you can give it a try.