Scott Hanselman

NuGet Package of the Week: ImageProcessor - lightweight image manipulation in C#

May 21, '14 Comments [38] Posted in NuGet | NuGetPOW
Sponsored By

I really enjoy image manipulation in code. Sure, resizing photos is fun in Photoshop, but there's something viscerally enjoyable when you change images with your own code.

I've talked about image resizing libraries like ImageResizer before, but there's certainly room for more than one. Today I want to showcase ImageProcessor, an open source "collection of lightweight libraries written in C# that allows you to manipulate images on-the-fly using .NET 4+." ImageProcessor is available on GitHub.

ImageProcessor

ImageProcessor methods include; Resize, Rotate, Rounded Corners, Flip, Crop, Watermark, Filter, Saturation, Brightness, Contrast, Quality, Format, Vignette, Gaussian Blur, Gaussian Sharpen, and Transparency.

ImageProcessor has a notable number of configuration options for web apps, and a supporting ImageProcessor.Web package as well. It's an impressive body of work.

I like this simple example of loading, resizing, and saving an image with their fluent API:

// Read a file and resize it.
byte[] photoBytes = File.ReadAllBytes(file);
int quality = 70;
ImageFormat format = ImageFormat.Jpeg;
Size size = new Size(150, 0)

using (MemoryStream inStream = new MemoryStream(photoBytes))
{
using (MemoryStream outStream = new MemoryStream())
{
using (ImageFactory imageFactory = new ImageFactory())
{
// Load, resize, set the format and quality and save an image.
imageFactory.Load(inStream)
.Resize(size)
.Format(format)
.Quality(quality)
.Save(outStream);
}

// Do something with the stream.
}
}

You can easily chain functions with the API, like tinting and constraning:

imageFactory.Load(inStream)
.Constrain(size)
.Tint(Color.FromArgb(255, 106, 166, 204))
.Format(format)
.Save(outStream);

When you add ImageProcessor.Web it adds caching that takes pressure off your web servers. You can easily add HttpHandlers to watermark an image, for example, and cache the result.

This is a library that has as a lot of potential. Since it's open source, I'm sure they'd appreciate help from the community! Personally, I think they could use more Unit Tests and more examples.

Head over to https://github.com/JimBobSquarePants/ImageProcessor and star this project! Get involved, file issues, and contribute! http://imageprocessor.org/

Related Links


Sponsor: Many thanks to Izenda for sponsoring the blog feed this week. Please do check out their Intuitive Ad Hoc Reporting with Stunning Visualizations - Embed real time dashboards into your ASP.NET applications for easy, custom reports across all devices. Download a FREE TRIAL of Izenda Today!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Thursday, May 22, 2014 3:50:12 AM UTC
Can it be used on windows phone?
Denis Ionov
Thursday, May 22, 2014 5:51:48 AM UTC
By the look of things, this is using System.Drawing for parts of its work - so, no thanks, won't be using that on a web site.
Damien
Thursday, May 22, 2014 6:31:28 AM UTC
@Damien - actually, the modules are excellent for website use with things like image cropping and the other manipulation that can be done. The Web module makes it very easy, for example, to create a custom crop of any image in MVC - and the resulting images can be cached so for performance (also built in).

Umbraco now incorporates this functionality by default for it's image cropper, and there's no drama at all from a performance point of view...
Thursday, May 22, 2014 7:08:35 AM UTC
It might be nice to add a link to a wrapper I am working on: https://github.com/kipusoep/ImageProcessor.Core
This wrapper can be used to create strongly typed ImageProcessor URLs, so you don't have to look-up the URL querystring parameters you want to use :-)
Thursday, May 22, 2014 7:58:58 AM UTC
My point was that, from day 1 (so far as I can recall), this notice has existed about the System.Drawing namespace:

Classes within the System.Drawing namespace are not supported for use within a Windows or ASP.NET service.
Attempting to use these classes from within one of these application types may produce unexpected problems,
such as diminished service performance and run-time exceptions.
Damien
Thursday, May 22, 2014 9:25:22 AM UTC
As far as I know this library is now being used in the core of the Umbraco CMS too.
Thursday, May 22, 2014 10:08:26 AM UTC
Hi all, I'm the author of ImageProcessor. Thanks Scott for sharing my work, it's a real honour :)

Damien, that message on the documentation doesn't actually mean what you, or I when I first read it, think it does; it's actually a lot less severe. There's an explanation here from Cheryl Simmons, the person who added the statement to the documentation.

http://blogs.msdn.com/b/winformsue/archive/2007/05/14/what-does-not-supported-mean.aspx

I've worked incredibly hard to ensure that the libraries not only work but are efficient also. I use it in every site I work on without issue and as Robert said, Umbraco use it in every install of their CMS. It's well tested.

That said...

I would love to get a v2 going which would be truly cross platform (Sorry Denis, not yet), to do that I'd probably have to shift things across to using the WritableBitmap class.

I can't do all that on my own though.

Scott's right. I would definitely appreciate and would most welcome contributions from the community. It's essentially a one man show and it takes up an insane amount of my time. I need help writing unit tests (I know sorry), updating documentation, people to test the libraries across operating systems and devices, sense check some of my code, and a whole bunch of other stuff.

I'm totally committed to open source and I'd really like to build something special that will make our collective developer lives a little bit easier.

Thanks again Scott for sharing my work. You're an absolute legend!
Thursday, May 22, 2014 12:15:16 PM UTC
It seems to be a FORK of http://dotnetimageprocessor.codeplex.com/, isn't it?

Therefore I assume the license should be Microsoft Public License (Ms-PL) as this says:

(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution.
Weh Pung
Thursday, May 22, 2014 12:51:12 PM UTC
Well you'd be assuming wrong I'm afraid. It's not a fork of anything, I started working on the guts of this long before I added it to github.
Thursday, May 22, 2014 1:10:45 PM UTC
Sweet!
Btw, chaining using syntax is more elegant syntax IMHO:

using (MemoryStream inStream = new MemoryStream(photoBytes))
using (MemoryStream outStream = new MemoryStream())
using (ImageFactory imageFactory = new ImageFactory()) {
...
}
Thursday, May 22, 2014 1:36:13 PM UTC
I agree with Patrick. Nested usings are ugly! The example would be much more impressive if you chained them.
Thursday, May 22, 2014 2:44:24 PM UTC
Hi Scott,

I really enjoy your posts on these nuget nuggets (yes, pun intended). What would make them even more awesome would be some way to see where (on what platforms) the package can be used.

in this case the author of the package already clearly detailed this, but I hope you can take my suggestion for the next package you highlight (and the next and the next and the...)

Keep up the good work!
Robert Echten
Thursday, May 22, 2014 3:17:39 PM UTC
I wonder if this gets around the annoying problem of Apple images using a flag to describe orientation instead of writing the bytes in the right order.
Thursday, May 22, 2014 9:04:00 PM UTC
@Jeff: Not using the flag and using JPEG would mean re-compressing the image and as Scott mentioned, JPEG is lossy.
Jesper
Thursday, May 22, 2014 9:43:22 PM UTC
Please developpers look at the code this is clearly a clone of http://dotnetimageprocessor.codeplex.com but the author denies.

For example please compare

http://dotnetimageprocessor.codeplex.com/SourceControl/latest#Main/ImageProcessor/Filters/OctreeQuantizer.cs

With

https://github.com/JimBobSquarePants/ImageProcessor/blob/master/src/ImageProcessor/Imaging/OctreeQuantizer.cs

Compare methods in source they are (at least today) all methods completely identical down to all the comments !
Weh Pung
Friday, May 23, 2014 12:01:04 AM UTC
Weh... I really don't understand what your problem is with me. That class has been going around for years. It's been the standard since 2007 when it was refactored by Brendan Tomkins, which is based on an earlier Microsoft article. Almost everybody uses it.

http://codebetter.com/brendantompkins/2007/06/14/gif-image-color-quantizer-now-with-safe-goodness/

I've had a look at the other library. I hadn't heard of it before you commented. Our methodologies are entirely different, the process is entirely different. The only thing in common is that they both share the name ImageProcessor, which considering the name describes the function is not remarkable.

Don't be a that kind of person. I've tried to do something that is for the benefit of the community, something free that always will be. I've worked incredibly hard in order to do so and it's really quite unpleasant to read someone attacking me. Use my work if you want to, don't if you don't but don't question my integrity or belittle my effort.
Friday, May 23, 2014 4:09:01 AM UTC
Scott I'm you're biggest fan please help me find Julia lerman so I can get the gang back together on plural sight. So I can get her autograph. On my entity programming entity framework version four book. What was th at project wih the Twitter Julia help me Julia lerman you're myn only hope tapirs code

Sent by the department of veteran affairs.
Friday, May 23, 2014 4:37:05 AM UTC
Is it possible to fix, after 10+ years the leaking system.drawing image read, write, allocate, etc parts? Clearly, MS does not take server side software seriously.
Ron
Friday, May 23, 2014 7:10:43 AM UTC
@James: So please excuse me!

I was just looking for your code because of scotts post and the similar name led me to navigate to the codeplex project.

Then something very unlikely happened: I intuitively browsed to the files Quantizer.cs and OctreeQuantizer.cs in both projects and saw this very high similarity.

Possibly you can understand my assumption. I should have compared much more! So please excuse my blame!

Weh Pung
Friday, May 23, 2014 8:22:15 AM UTC
@James : it looks like your library does not use the full framework, so I have created a pull request to switch to Client Profile. It would allow us to include this lib in a WPF application, widely distributed (about 200-500 users/day). It looks like an awesome library!
Friday, May 23, 2014 9:36:08 AM UTC
@JamesSouth I really appreciate your time and effort gone into this, it looks fantastic. Please ignore the haters! developers should be helping each other to improve functionality and performance NOT discredit ones work.

Great work, I will certainly use this!

Regards,
Friday, May 23, 2014 9:36:47 AM UTC
I wonder how this compares to something like http://www.imagemagick.org/

I use Image Magick because it's the only solution that maintains quality and also keeps colour profiles of photos exported from Photoshop preventing it from losing colour.

Friday, May 23, 2014 10:05:25 AM UTC
@James I really appreciate your work. Definitely what I was looking for. Keep up the good work.
Friday, May 23, 2014 10:41:28 AM UTC
I had always treated the "System.Drawing + ASP.NET" warnings as overstated and been happily rendering images on websites with System.Drawing for years.

Then I tried Azure Websites. Oh dear.

You really can't use System.Drawing reliably on Azure Websites - so there might be a problem for this library in that context - has anyone tried it? (Other flavours of Azure do not have this problem)

Will
Friday, May 23, 2014 12:23:17 PM UTC
System.Drawing is the core part of many projects I have had in production for years. It does some things remarkably well compared to several paid libraries. It has worked well for the most part for me. I wouldn't hesitate to use this package either. The main things I found out the hard way with System.Drawing is to make sure you dispose properly (which goes for anything that implements Idisposable).
Friday, May 23, 2014 1:49:56 PM UTC
@weh That's cool, I'm glad you had another look.

@Thomas Thanks! That's excellent. I'll merge, test and if everything is ok I'll make sure it is in the next release.

@Tez, thanks for your support, I think it was just a misunderstanding.

@Phillip I use the Image.FromStream Method (Stream, Boolean) which is supposed to preserve any embedded colour information. I haven't noticed any discernible colour loss in my tests. Other than that I don't know right now.

@Rizwan thanks :)

@Will Azure Websites don't seem to support GDI+ so unfortunately not in this instance. I can't seem to find any details on whether they support WIC.

@Ben, Thanks, I worked hard to ensure that streams were getting preserved and what should be getting disposed was getting so at the correct point. Part of my reason for writing the library was seeing so many developers get working with System.Drawing so wrong. Lot's of poor examples on blogs all over the internet.
Friday, May 23, 2014 2:43:48 PM UTC
@James - In many ways, I'm pleased to hear that your library chokes on Azure too, as I think the greatest hope of getting the Azure / GDI+ issue fixed may now be upon us!

Scott recommending a library which won't actually run on his department's favourite platform could be just what's needed to get it sorted out.

Scott, could we have a nice piece about IPv6 next, please... ;-)

Will
Friday, May 23, 2014 3:21:36 PM UTC
@Will Actually I might be wrong about my library choking. When you are signed in to Azure websites it offers both Umbraco, and MVCForum as applications you can install. If it's working in both of those packages (which I would only assume it does because it's built into the core of both) then it's running fine. I'll have to test first hand though.
Friday, May 23, 2014 4:14:58 PM UTC
@James - it's an odd and unpredictable (to me, at least) subset of stuff which works/doesn't work. For example, you can do a DrawString of a vertically-centred line of text, but you can't call Font.Height - clearly the system is able to calculate the height of the text, but it just can't tell me via System.Drawing.

I guess you've just been lucky enough to avoid the broken bits. (Or perhaps the broken bits are very few, but I've been unlucky to hit them the moment I started looking at Azure...)

Unfortunately I've been unable to find any coherent guidance from MS on what the problem is - various discussion threads around the place mostly read like some kind of Onion-style parody of an MS Connect thread...
Will
Friday, May 23, 2014 5:15:18 PM UTC
@Jesper: I know how JPEG works... that wasn't the point. Apple doesn't record the bits from the iPhone camera in the right orientation ever. It records them the same regardless of orientation then sets the flag. If this library doesn't recognize the flag, it throws it away, meaning if you resize a photo, the orientation data goes away and it ends up upside down or sideways.
Friday, May 23, 2014 6:32:03 PM UTC
@Will, that sucks. Maybe as a community we can figure out what we need to know.

@Jeff I wasn't aware of this issue. (I don't own any apple products to test with). ImageProcessor at present discards the EXIF data (to keep sizes small) so yeah, it will get rotated. Turns out Windows Explorer and Picture viewer in 7 and below ignored it also.

I've been pondering adding the ability to copy over the data with an overload to the ImageFactory class. I think it's a necessity now. Would you care to raise it as an issue on my github page?
Friday, May 23, 2014 9:28:22 PM UTC
@James - well, this discussion has been useful (to me at least, sorry to everyone else), in that it's provoked me to look at the framework source for where the crashes have been occurring, and I realise then when we call Font.Height, the framework attempts to use a screen DC, which is what fails on Azure. If, instead, we call GetHeight(ourGraphicsObjectWhichDoesn'tWrapAScreenDC), then I don't have a problem because it uses the DC from our own (bitmap-backed) Graphics object.

So as long as we don't call stuff which implicitly refers to a screen DC, then we should get a bit further. It may well be that Fonts are the main culprit here (I see other references to the screenDC in the Font.cs) which is why it's not a problem for your library.

Thanks for unwittingly providing the impetus to look into this more!

Will
Monday, May 26, 2014 11:19:11 AM UTC
@Jeff: Yes, fine, they could record the data in the right orientation (presumably at the cost of speed). As you say, the orientation field is part of the JPEG standard and it is outrageous that it's not followed by all libraries. If these libraries chose not to follow other parts of the JPEG standard, they'd be at fault for that too.
Jesper
Tuesday, May 27, 2014 5:10:24 PM UTC
I don't think it's a part of the JPEG standard... it's only a tag in EXIF data and there's a lot of debate over what EXIF tags are truly "standard." I think you're oversimplifying the problem.
Wednesday, May 28, 2014 10:26:25 PM UTC
Well done James! Just what I was looking for. I'll look for opportunities to give back as I use it.
Sunday, June 01, 2014 8:33:20 PM UTC
Thanks for the continuing support folks it's so great to hear from you! :)

You'll be happy to no I've just released an updated version which now supports EXIF metadata preservation. I've also switched the build to use the client profile.

I'm working hard to improve the documentation also.

Check it out! http://imageprocessor.org/
Monday, June 02, 2014 3:54:52 PM UTC
Excellent work James, I have used a few other third party tools to deal with image processing but I was always left feeling something was missing.

I have literally just finished integrating this in to my site and its perfect, only thing I need to do now is set the orientation because some images it uploads tend to flip from vertical to horizontal not 100% why its doing that but I shall find out :)
Scott Atkinson
Friday, June 27, 2014 7:08:30 PM UTC
ImageProcessor 4 President!!
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.