.Net Image Processor

The main reason this project was created was to provide a simple wrapper for producing high-quality thumbnail images using GDI+ and .Net. The result is the beginnings of a processing framework which can be used to apply one or more filters to a single image in a processing queue. An interesting side-effect is that it can also easily be used to convert an image from one file format to another!

Creating an image processor

The image processor and its related objects live in the Simplicode.Imaging namespace:
Simplicode.Imaging.ImageProcessor processor = new Imaging.ImageProcessor();

File format conversion

The image processing queue is kicked off by a call to ProcessImage() .
To convert an image from PNG to Jpeg, simple process the two images using the overload which takes and input and output filename. The output file format is inferred from the filename extension, and the default Jpeg quality setting is 80%:
processor.ProcessImage("testimage.png", "testimage_converted.jpg");

You can force the output format and Jpeg quality settings by configuring the OutputFormat and JpegCompression properties. This can be useful when using the process overload which takes an input and output stream, where the output format cannot be inferred:
processor.OutputFormat = Imaging.OutputFormat.Jpeg;
processor.JpegCompression = 90L;
using (var inputStream = new FileStream("testimage.png", FileMode.Open))
{
  using (var outputStream = new FileStream("testimage_converted_stream.jpg", FileMode.Create))
  {
    processor.ProcessImage(inputStream, outputStream);
  }
}

GIF support

You can also output to GIF and specify the colour resolution, as in the following example which converts a test PNG image to GIF using a pallete of 128 colours:
processor = new Imaging.ImageProcessor();
processor.GifColours = 128;
processor.OutputFormat = Imaging.OutputFormat.Gif;
processor.ProcessImage("testimage.png", "testimage.gif");

Resizing an image

To resize an image, simply add a Filters.ResizeFilter into the Filters collection on the processor. This can also be done during construction of the processor if desired:
var resizer = new ResizeFilter(400, 400);
processor.AddFilter(resizer);
processor.ProcessImage("testimage.png", "testimage_resized.png");

The default behaviour here is to resize the image to a maximum of 400x400, keeping the aspect ratio of the original image. There, if your input image has an aspect ratio of 4:3, then using this configuration the actual size of the output image will be 400x300 pixels.

To change the behaviour of the resizer, use the next overload which allows you to specify how the image is resized. For example, using ResizeMethod.Absolute will give you the output size you desire, but the image may appear stretched as it adapts to a new aspect ratio.

Cropping

To achieve the exact output size you want and also avoid stretching the image, you can use ResizeMethod.Crop, which will (by default) take the middle portion of the image if the difference between the input and output aspect ratios are such that stretching would normally occur. It will effectively give you the image size you want, but chopping off the top and bottom (or left and right, depending on the ratio difference) to compensate.

You can use an additional overload to specify an anchor, which can be useful if you know that your area of interest is always in a particular part of the image (headshots, for example):
processor.Filters.Add(new ResizeFilter(400, 300, ResizeMethod.Crop, AnchorLocation.TopMiddle));

Currently the anchor location is only used if ResizeMethod is set to ResizeMethod.Crop; it is ignore in all other cases.

Resize helper

If a simple image resize is all you need, you can also use a static helper method on the ImageProcessor class to resize an image:
Simplicode.Imaging.ImageProcessor.ResizeImage("testimage.png", "testimage.jpg", 400, 300);

This will resize an image using all the same default settings as when using filters normally. There are also optional parameters which allow you to specify some of the more detailed options:
Simplicode.Imaging.ImageProcessor.ResizeImage("testimage.png", "testimage.jpg", 400, 300, 
  method: ResizeMethod.Crop, jpegQuality: 50L, anchorLocation: AnchorLocation.TopMiddle, outputFormat: Imaging.OutputFormat.Jpeg);

Edge Detection

To perform edge detection, simply use the built-in edge detection filter:
processor = new Imaging.ImageProcessor(new EdgeDetectionFilter());

This applies edge detection to an image, using a black background and white lines. You can specify the colours, and also the line threshold during construction or by setting the relevant properties:
processor = new Imaging.ImageProcessor(new EdgeDetectionFilter(10, Color.Red, Color.Yellow));

Filter chaining

A feature of the library is the ability to 'queue up' several filters and execute them in one go. Taking all of the above examples, we can apply some edge detection to an image and then resize it as in the following example:
processor = new Imaging.ImageProcessor();
processor.Filters.Add(new EdgeDetectionFilter(7));
processor.Filters.Add(new ResizeFilter(200, 200));
processor.ProcessImage("testimage.png", "testimage_chained.jpg");

The image is first processed by the edge detection filter and then the result is passed on to the resize filter.

Watermarking

The library supports two types of watermarking - text and imaging. To apply either type, use the TextWatermarkFilter or ImageWatermarkFilter accordingly.

TextWatermarkFilter
To apply a text watermark to the processed image, add a TextWatermarkFilter into the processing chain. You can specify the text, font size, colour, style and anchor location when creating the class, like so:

processor = new Imaging.ImageProcessor();
var textWatermark = new TextWatermarkFilter("Copyright (C) Some Company 2011")
{
  FontStyle = FontStyle.Bold,
  FontName = "Arial",
  FontSize = 24,    
  Offset = new Point(-20, -20)
};
textWatermark.Opacity = .5f;
processor.AddFilter(textWatermark);
processor.ProcessImage("testimage.png", "testimage_text_watermarked.png");

ImageWatermarkFilter
Image watermarking allows you to specify another image to watermark the processed image with. This simple overlays the processed image with another image (supporting transparency, if applicable), and again you can specify the anchor location:

processor = new Imaging.ImageProcessor();
var watermark = new ImageWatermarkFilter("watermark.png", AnchorLocation.BottomRight);
watermark.Offset = new Point(-20, -20);
watermark.Opacity = .3f;

processor.AddFilter(watermark);
processor.ProcessImage("testimage.png", "testimage_image_watermarked.png");

Both types of filters allow you to specify the opacity (in the range 0 to 1, 0 being completely transparent) and an offset.

Creating filters

A powerful feature of the architecture is the ability to easily create filters, simply by implementing IImageFilter. Your filter can then be injected into the pipeline.

Currently the interface just has a single method: ProcessImage, which takes an instance of a GDI Image and returns another image which has had some processing done to it. You are free to apply your own image processing, without worrying about how the images get there in the first place, what format they need to be in how they chain to other filters.

A more complete example of creating custom filters is forthcoming.

Last edited Aug 7, 2011 at 12:15 PM by elkdanger, version 6

Comments

No comments yet.