Click here to Skip to main content
Click here to Skip to main content

ImageTraverser

, 19 Apr 2007
Rate this:
Please Sign up or sign in to vote.
A pointer-based class for retrieving and settings individual image pixels

Screenshot - demo.jpg

Introduction

Microsoft's System.Drawing.Bitmap class is deceptively simple - just create a Bitmap from a file, then use the GetPixel() and SetPixel() methods to manipulate the image, right? Unfortunately, these two methods are terribly slow, so a lower-level traversal via pointers is necessary for decent performance (a 10x - 75x improvement in my tests!).

However, there is a reason that the .NET language designers are steadily moving away from pointers - code that utilizes them is usually brittle and error-prone, even when run under the CLR. Therefore, to minimize and isolate the use of unsafe code in my projects, I have encapsulated the necessary unsafe pointer code in this class, plus I have added several handy methods for dealing with image pixels.

The end result is a robust class for traversing images (ie, retrieving and setting individual pixels) - hence the moniker ImageTraverser.

Background

Unsafe Code

"Unsafe" code blocks in C# are blocks of code that use pointers. While C# lets you leverage the power of pointers, it does retain a few safety features, such as:

  • You may not create a pointer to a managed type (compiler error)
  • You may not access some of the memory you're not supposed to (runtime error - see MSDN for more details)

While you may not create pointers to managed types, several of the basic types such as byte, int, bool, etc. are "special" types that exist in both the managed and unmanaged worlds, so the system allows you to point to those types without issue.

Running unsafe code requires that your Assembly be fully-trusted (which it probably is, unless you're running on a hosted machine somewhere).

Please see this article or search MSDN for more information on using unsafe code.

Image Processing

If you are new to image processing or just need a review, this page has some good diagrams and explanations. CodeProject also has several articles on this topic.

Using the Code

The zip file contains documentation in the MSDN format, so that is the best resource for method and property information. Here are some of the more important members however:

Properties

  • Image - the Image (or Bitmap) object to traverse. On Set, ImageTraverser actually creates a copy of the passed Image and works exclusively with that, leaving the original untouched. In fact, it does not even retain a reference to the original Image object. On Get, ImageTraverser returns a COPY of the underlying Image object, so use this accessor sparingly!
  • Image____ (Height, PixelFormat, Scan0AsBytePointer, Scan0AsIntPointer, Size, Stride, Width) - Retrieves the corresponding property from the underlying Image object. You should use these accessors rather than using ImageTraverser.Image.______ because each call to ImageTraverser.Image actually returns a COPY of the underlying Image object.

Methods

  • this[int x, int y], this[Point location] - Returns the int value of the pixel at the indicated position
  • GetPixel(int x, int y), SetPixel(int x, int y, Color value) - Gets or Sets the Color of the specified pixel. These methods are just wrappers around the indexers, but they are included for convenience because their signatures match those of Bitmap.GetPixel()/SetPixel(). These are the only methods that return Colors rather than ints.
  • GetRow(int row), SetRow(int row, int[] values) - Gets or Sets the int values of the pixels in the specified row
  • ToArray() - Returns a two-dimensional int array filled with the values of all the pixels in the Image

Interfaces

ImageTraverser implements both the IDisposable and IEnumerable interfaces, making it easy to use with "using" and "foreach" statements. The enumeration is pixel-by-pixel left-to-right across each row, top-to-bottom, and returns a simple Pixel class that contains a Point indicating the position in the Image and an int representing the color value of that pixel.

Points of Interest

Ints vs Colors

ImageTraverser returns ints rather than Colors because the Color.FromArgb(int) call is actually relatively time-consuming. It is much quicker just to return the raw ints because those are what is read directly from memory. You can easily make the Color.FromArgb(int) call yourself if you need an actual Color struct.

PixelFormats

ImageTraverser works exclusively in the 32bppArgb PixelFormat. However, you can pass in Images with different bit-depths and the class will convert its internal copy to 32bpp. Be aware then that even if you pass in, say, a 1-bpp Image, you will get back 32bpp values when traversing it and when calling ImageTraverser.Image.

Pixels, Rows, Arrays, oh my!

ImageTraverser supports several different views of the underlying Image - by individual pixel, by row, or as an entire two-dimensional array. This is useful because some algorithms operate pixel-by-pixel, others row-by-row, others over the entire image - just use whichever method makes the most sense in your project. There are some performance differences between these methods (see below), but all are MUCH faster than Bitmap.GetPixel()/SetPixel().

Performance

In the introduction, I claimed a 75x speed increase over the BCL's Image.GetPixel()/SetPixel() methods. Here are the numbers to back that up.

I ran several tests to compare the time it takes to read every pixel in the Image (repeated in a loop 100x). The Demo zip file above is the tester program - its purpose is to create a negative of the image, and it lets you specify the number of trials and iteration method, then reports the time back. Note that the time reported is the "iteration" time only and will not match the wall time because in-between iterations, time is spent updating the GUI (which can be quite psychedelic).

Here are the times I got on my machine:

Access Method Time per 100 Traversals (seconds)
IT Enumerator3.5152650
IT Indexer1.0625000
IT Array.7500000
IT Scan0 Ptr.4843750
IT Rows.5781250
IT GetPixel2.1562500
MS GetPixel35.6250000

ImageTraverser's fastest traversal method (pointer) is ~75x as fast as the Bitmap.GetPixel() method, and ImageTraverser's slowest traversal method (enumerator) is still ~10x as fast as Bitmap.GetPixel()!

History

4/17/2007 - initial release

License

This article, along with any associated source code and files, is licensed under The MIT License

Share

About the Author

Jacob Klint
Software Developer
United States United States
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 PinmemberBigdeak8-Sep-10 19:35 
QuestionDetecting Color Ranges Pinmemberrfrank535624-Feb-10 5:19 
Hi Jacob
 
I have some new code (for me) that uses the getPixel method in vb.net - I have a large library of aerial photos (see AerialGISTechnology.com) and need to scan for targets. I don't expect to be able to detect the targets through code, so I manually locate them, then place a yellow[255,255,0] square on a target location (using a photoshop script) and then later look at the image to recover the upper left pixel address [x,y] of the square which then through some real magical code [Smile | :) ] results in a lat/lon location of that pixel. This works sorta ok until the jpg compressor gets involved, and during the compression process, the RGB values are slightly altered. Yellow becomes [255,254,1] or [254,253,3]. So - the test for a fixed yellow fails randomly due to data. Is there a way to test for a color range - I could stand a few false positives, but could not accept misses, as there is an evaluation step after the data extraction that will catch the false positives.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140821.2 | Last Updated 19 Apr 2007
Article Copyright 2007 by Jacob Klint
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid