ImageProcessor Core

So its been a while...

It's been roughly a year since my last blog post. I wrote it about a recent ImageProcessor release and opened the last paragraph with the following.

For me this release marks the last I shall actively work on in this major version. It might well be the last I do ever depending on what Microsoft do with graphics with the next .NET releases. I've fallen out of love with the open source community a little over the years due to the lack of collaboration I've received on this project and others.

Sad eh?... Well it turns out I lied. Not only have I released several updates to the V2 branch with some great contributions from the community, but I've been hard at work on something a little special... ImageProcessor Core

A Little History

In 2014 Microsoft blew everyone's minds by announcing that they were building a new, open source, cross-platform framework containing a subset of the full .NET Framework. Everyone rightfully got very excited but a few people (me included) started to worry about the lack of discussion around a graphics API. Issues were opened up on the Github repo and quite a few people pitched in with their two cents.

Nobody was doing anything though and it was very frustrating for me to watch.

You see, I have a dream...

Toy Story meme

I originally wrote ImageProcessor to make it easier for developers to do common tasks with System.Drawing. I kept seeing blog posts with copy/pasted code examples full of memory leaks and slow code and I wanted to make a difference and give back to a community I learned from.

ImageProcessor Core is an extension of that idea.

The Elevator Pitch

Common imaging tasks should be accessible to all developers. If you want to resize or crop an image you should be able to use a simple API and do so easily. You shouldn't have to worry about memory leaks and you shouldn't have to worry about binding to native libraries or deploying in different environments. It should be cross-platform and it should Just Work ™

Oh and of course it needs to be scalable and allow for complex operations and different pixel formats. You know... Simple stuff!

ImageProcessor Core is designed with that in mind.

So what can it do? As of writing ImageProcessor Core is still very much in alpha status but it turns out quite a lot.

I have....

  • Algorithms for common operations. Resize, Crop, Flip, Rotate, Edge Detection and so on.
  • Almost complete support for four different codecs. Bmp, Png, Gif, and of course the most complicated but commonplace of all Jpeg.
  • Full EXIF read and write capability using a simple API.
  • A generic image class that is signature compatible with Microsoft XNA Game Studio and MonoGame.
  • A snazzy new logo that I designed myself :)

And much much more..

Example Time

To demonstrate the API I will show you some code demonstrating some common tasks. Here's how to resize an image using the default bicubic resampler.

using (FileStream input = File.OpenRead("foo.jpg"))
using (FileStream output = File.OpenWrite("bar.jpg"))
{
    Image image = new Image(input);
    image.Resize(image.Width / 2, image.Height / 2)
         .Save(output);
}

Want to use a different resampler? We've got you covered.

using (FileStream input = File.OpenRead("foo.jpg"))
using (FileStream output = File.OpenWrite("bar.jpg"))
{
    Image image = new Image(input);
    image.Resize(image.Width / 2, image.Height / 2, new MitchellNetravaliResampler())
         .Save(output);
}

How about companding the image to resize with gamma correction?

using (FileStream input = File.OpenRead("foo.jpg"))
using (FileStream output = File.OpenWrite("bar.jpg"))
{
    Image image = new Image(input);
    image.Resize(image.Width / 2, image.Height / 2, true)
         .Save(output);
}

How about setting individual pixel values?

Image image = new Image(400, 400);
using (PixelAccessor<Color, uint> pixels = image.Lock())
{
    pixels[200, 200] = Color.White;
}

How easy is that?

The Image class is actually an implementation of a generic class Image<TColor, TPacked> where TColor implements an interface identical to the Microsoft XNA Game Studio and MonoGame IPackedVector<T>. This allows the library to work well with those frameworks and tightly control memory usage. Underneath the hood it's actually an Image<Color, uint>.

Performance

A lot of developers say that C# can never be truly fast. A lot of developers are wrong. I've got a long way to go yet but I've been able to get some pretty decent performance going in this early stage.

Check out this benchmark using the awesome BenchMarkDotNet comparing a 2000x2000 pixel image downsampled to 400x400 pixels.

Host Process Environment Information:
BenchmarkDotNet-Dev.Core=v0.9.9.0
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-4712HQ CPU 2.30GHz, ProcessorCount=8
Frequency=2240907 ticks, Resolution=446.2479 ns, Timer=TSC
CLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT]
GC=Concurrent Workstation
JitModules=clrjit-v4.6.1080.0

Type=Resize  Mode=Throughput
                        Method |     Median |    StdDev | Scaled | Scaled-SD |
         System.Drawing Resize | 66.2606 ms | 1.2463 ms |   1.00 |      0.00 |
     ImageProcessorCore Resize | 56.4141 ms | 1.0673 ms |   0.85 |      0.02 |

Other methods like Crop are much faster and Get/Set pixel is ~94% faster than the System.Drawing equivalent on my test machine. I'm very happy with progress so far and I'm sure I can go much faster.

So What's The Plan?

It's been a very long alpha but I think the API is pretty solid so I shall be moving out of alpha and into beta status soon. Beta releases will be hosted on Nuget as opposed to the current MyGet server though you shouldn't use it in production environments until I reach Release Candidate status.

In beta I shall be concentrating on filling out missing encoder support (you can help!) and really cranking up the performance.

I'd love to get 2D drawing added to the API but that's probably better off in V1.1 as there's a lot of work involved.

Want To Help?

Please do... Spread the word, contribute algorithms, submit performance improvements, unit tests. Play around with the API and tell me what is missing.

If you know someone who works in image processing please try to convince them to get involved.

There's simply no reason why as a community we cannot build an absolute first-class imaging API that can benefit all levels of developer.

So come on, visit the repo and get mucked in!

Please let me know what you think in the comments below.

Update

Shannon below wanted to see the logo so drink it in. Pure design... Take that Instagram!

ImageProcessor Logo

comments powered by Disqus