tags:

views:

1266

answers:

7

I have written a web service to resize user uploaded images and all works correctly from a functional point of view, but it causes CPU usage to spike every time it is used. It is running on Windows Server 2008 64 bit. I have tried compiling to 32 and 64 bit and get about the same results.

The heart of the service is this function:

private Image CreateReducedImage(Image imgOrig, Size NewSize)
{
 var newBM = new Bitmap(NewSize.Width, NewSize.Height);
 using (var newGrapics = Graphics.FromImage(newBM))
 {
  newGrapics.CompositingQuality = CompositingQuality.HighSpeed;
  newGrapics.SmoothingMode = SmoothingMode.HighSpeed;
  newGrapics.InterpolationMode = InterpolationMode.HighQualityBicubic;
  newGrapics.DrawImage(imgOrig, new Rectangle(0, 0, NewSize.Width, NewSize.Height));
 }

 return newBM;
}

I put a profiler on the service and it seemed to indicate the vast majority of the time is spent in the GDI+ library itself and there is not much to be gained in my code.

Questions: Am I doing something glaringly inefficient in my code here? It seems to conform to the example I have seen.

Are there gains to be had in using libraries other than GDI+? The benchmarks I have seen seem to indicate that GDI+ does well compare to other libraries but I didn't find enough of these to be confident.

Are there gains to be had by using "unsafe code" blocks?

Please let me know if I have not included enough of the code...I am happy to put as much up as requested but don't want to be obnoxious in the post.

+4  A: 

Image processing is usually an expensive operation. You have to remember that a 32 bit color image is expanded in memory into 4 * pixel width * pixel height before your app even starts any kind of processing. A spike is definitely to be expected especially when doing any kind of pixel processing.

That being said, the only place i could see you in being able to speed up the process or lowering the impact on your processor is to try a lower quality interpolation mode.

Paul Sasik
+3  A: 

I know that the DirectX being released with Windows 7 is said to provide 2D hardware acceleration. Whether this implies it will beat out GDI+ on this kind of operation, I don't know. MS has a pretty unflattering description of GDI here which implies it is slower than it should be, among other things.

If you really want to try to do this kind of stuff yourself, there is a great GDI Tutorial that shows it. The author makes use of both SetPixel and "unsafe blocks," in different parts of his tutorials.

As an aside, multi-threading will probably help you here, assuming your server has more than one CPU. That is, you can process more than one image at once and probably get faster results.

Brian
+2  A: 

You could try

newGrapics.InterpolationMode = InterpolationMode.Low;

as HighQualityBicubic will be the most processor-intensive of the resampling operations, but of course you will then lose image quality.

Apart from that, I can't really see anything that can be done to speed up your code. GDI+ will almost certainly be the fastest on a Windows machine (no code written in C# is going to surpass a pure C library), and using other image libraries carries the potential risk of unsafe and/or buggy code.

The bottom line is, resizing an image is an expensive operation no matter what you do. The simplest solution is your case might simply be to replace your server's CPU with a faster model.

Ian Kemp
A: 

I suspect the spike is because you have the interpolation mode cranked right up. All interpolation modes work per pixel and BiCubic High Quality is about as high as you can go with GDI+ so I suspect the per pixel calculations are chewing up your CPU.
As a test try dropping the interpolation mode down to InterpolationModeNearestNeighbor and see if the CPU spike drops - if so then that's your culprit.
If so then do some trial and error for cost vs quality, chances are you might not need High Quality BiCubic to get decent results

zebrabox
A: 

When you write

I have written a web service to resize user uploaded images

It sounds to mee that the user uploads an image to a (web?) server, and the server then calls a web service to do the scaling?

If that is the case, I would simply move the scaling directly to the server. Imho, scaling an image doesn't justify it's own web service. And you get quite a bit unnecessary traffic going from the server to the web service, and back. In particular because the image is probably base64 encoded, which makes the data traffic even bigger.

But I'm just guessing here.

p.s. Unsafe blocks in itself doesn't give any gain, they just allow unsafe code to be compiled. So unless you write your own scaling routing, an unsafe block isn't going to help.

Pete
+1  A: 

You may want to try ImageMagick. It's free, and there is also a .NET wrapper: click here. Or here. Or you can send a command to a DOS Shell.

We have used ImageMagick on Windows Servers now and then, for batch processing and sometimes for a more flexible image conversion.

Of course, there are commercial components as well, like those by Leadtools and Atalasoft. We have never tried those.

Olaf