Sharing is caring
I was cleaning up among old projects today and stumble on some image manipulation stuffs. This was used in a web application to scale images in real-time on the server, storing them on disk and serve them to the web browser by calling a url. Instead of using the Windows GDI+, the Windows Presentation Foundation (WPF) was used to increase performance. Even if this isn’t a new thing I did some tests and think it still performs very well – well worth sharing. Sharing is caring.
Methodology
The approach is to use the ScaleTransform class, which scales an object in the 2-D x-y-coordinate system, and the TransformedBitmap class to apply this transform to an image. The first frame of the image itself will be extracted and injected into the bitmap transform along with the scale transform. The scaled image is further on encoded using the JpegBitmapEncoder class. This methodology is wrapped into a single ”chainable” class.
Usage
// Example 1
new ImageResizing("source/path/source-image.png")
.Resize(800, 0)
.Quality(70)
.Save("destination/path/scaled-image.jpg"); // Calling save will dispose
// Example 2
using (var resizer = new ImageResizing(imageSourcePath))
{
var resizedImage = resizer.Resize(120, 0);
var imageStream = resizedImage.ToStream();
var imageBytes = stream.ToArray();
// …
}
The code
Performance
To find out how this method performs I did a short comparison with the ImageResizer and the ImageProcessor. The task was to scale a 1600×1067 pixel jpeg image to a width of 800 pixels, one hundred times, using a compression rate of 70 and save it to disk. This was running in a Windows 10 virtual machine, on a MacBook Pro 2.3 GHz Intel Core i5 with 8 GB RAM.
ImageResizing using WPF
Average: 2.26 seconds
CPU: 40% (peak)
RAM: 42.6 MB
Resized image filesize: 32.7 KB
Test code:
for (var i = 0; i < 100; i++)
{
new ImageResizing("image/source/path/bird.jpg")
.Resize(800, 0)
.Quality(70)
.Save("image/destination/path/bird__ImageResizing_(WPF).jpg");
}
ImageResizer
Average: 6.12 seconds
CPU: 50% (peak)
RAM: 29.3 MB
Resized image filesize: 32.6 KB
Test code:
for (var i = 0; i < 100; i++)
{
ImageResizer.ImageBuilder.Current.Build(new ImageResizer.ImageJob
{
Instructions = new ImageResizer.Instructions
{
Width = 800,
JpegQuality = 70
},
Source = "image/source/path/bird.jpg",
Dest = "image/destination/path/bird__ImageResizerNET.jpg"
});
}
ImageProcessor
Average: 12.52 seconds
CPU: 79% (peak)
RAM: 31.7 MB
Resized image filesize: 33.1 KB
Test code:
for (var i = 0; i < 100; i++)
{
var photoBytes = File.ReadAllBytes("image/source/path/bird.jpg");
using (var inStream = new MemoryStream(photoBytes))
using (var imageFactory = new ImageFactory(preserveExifData: true))
{
imageFactory.Load(inStream)
.Resize(new Size(800, 0))
.Format(new JpegFormat { Quality = 70 })
.Save("image/destination/path/bird__ImageProcessor.jpg");
}
}
Conclusion
Resizing images using Windows Presentation Foundation (WPF) gave the best performance: 2.7 times faster than ImageResizer and 5.5 times faster than ImageProcessor. WPF and ImageResizer gave the best image quality and will share first place. ImageProcessor had some trouble with the darker areas of the bird. WPF was consuming the least CPU but the highest RAM.
As I sad in the beginning of this article: Image resizing using WPF isn’t a new thing. But it performs well and produces great result. Maybe something to consider next time you need to have performant image resizing in your application?
If someone thinks I should have done the performance setup different, please let me know.