C# .Net provides a good support for processing on the image, and the purpose of this article is not to give you a lot of insight into the image processing, rather it is written to help you start your image processing career using C#. The article contains:
- Displaying Images on a Dialog Box
- Basic Image Library Documentation
- Locking and Unlocking the image
- Basic Layout of the Locked Array
- Iterating through the Image
Displaying Image on the Dialog Box
The image can be displayed on the dialog box using C# by using the PictureBox tool from the Toolbox. To see this lets create a project and display an image in it using PictureBox. To do this, follow these steps:
- Click File>New>Project, the following dialog box will appear:
- Enter the desired project name, location and then click OK. Now a project is created.
- This will show the default form of the project.
- Select the PictureBox from the Toolbox and draw it on the form of the desired size and adjus its properties as wanted.
- This will add an instantiation of the PictureBox class’s instantiation in the Form1 class.
- Create an object of the class Bitmap by giving the address of the desired file in the constructure as:
Bitmap bmp = new Bitmap( "c:/images/image.gif" );
- Now assign the Image member of the class the bmp objec as:
picturebox1.Image = bmp;
- Call the Invalidate( ) function of the form class.
- Now the image will be shown in the PictureBox in during execution in the similar way as shown in the diagram below:
Basic Image Library Documentation
The microsoft .net framework provides a rich library for working in any field. It also gives the support for processing on the images. The basic library support is documented below:
PictureBox class:
ClientRectangle: ClientRectangle is a public property representing a rectangle that represents the client area of the control. It does not include the elements such as scroll bars, borders, title bars, and menus. It only represent the area in which the image is drawn actually.
Image: The public property representation of the Image class to display. The Image property is set to the image to display. You can do this either at design time or at run time.
SizeMode: The property showing how to display the image.
Refresh: Function that forces the control to invalidate its client area and immediately redraw itself and any child controls.
Update: Function that causes the control to redraw the invalidated regions within its client area.
Bitmap Class
The Bitmap class is the generalization of the Image class. Note above that, the PictureBox class contains a refrence of the image class, but the Image class is an abstract class, so its objects can be instantiated and PictureBox objects contains the instantiations of the Image class’s derived classes, one of which is the Bitmap class..
Constructor: Bitmap class contains 12 constructors that construct the Bitmap object from different parameters. It can construct the Bitmap from another bitmap, and the string address of the image. It also provides the constructors the Bitmap object from Bitmap( int width , int height ).
PixelFormat: PixelFormat is the public property of the image of this image object.
Clone: This is the overloaded function of the Bitmap class inherited from the Object class that returns the copy of the object on which the function was orignaly invoked.
GetPixel: The public function that get the color of the specified pixel.
LockBits and UnLockBits: The public functions in the class that locks and unlocks the specified area of the image into the memory. The paramenters of the LockBits function are, a Rectangle object specifying the area to be Locked, then 2 integers specifying the access level for the object and the other showing the format of the image. This is done through two enumirations like ImageLockMode, and PixelFormat. They are narrated after this class. The LockBits returns the object of the BitmapData class and UnLockBits takes the object of the BitmapData class.
BitmapData LockBits( Rectangle , ImageLockMode , PixelFormat );
SetPixel: The public function that sets the color of the specified pixel.
ImageLockMode Enumiration
Specifies flags that are passed to the flags parameter of the LockBits method. It has four members.
ReadOnly: Specifies that a portion of the image is locked for reading.
ReadWrite: Specifies that a portion of the image is locked for reading or writing.
UserInputBuffer: Specifies that the buffer used for reading or writing pixel data is allocated by the user.
WriteOnly: Specifies that a portion of the image is locked for writing.
BitmapData class
Scan0: The address of the first byte in the locked array. If whole of the image is locked, then it is the first byte of the image.
Stride: The width, in bytes, of a single row of pixel data in the locked array. This width is a multiple, or possibly sub-multiple, of the pixel dimensions of the image and may be padded out to include a few more bytes. I’ll explain why shortly.
Width: The width of the locked image.
Height: The height of the locked image.
PixelFormat: The actual pixel format of the data.
PixelFormat Class
The pixel format defines the number of bits of memory associated with one pixel of data. In other words the format defines the order of the color components within a single pixel of data. Normaly, PixelFormat.Format24bppRgb is used. PixelFormat.Format24bppRgb specifies that the format is 24 bits per pixel; 8 bits each are used for the red, green, and blue components. In the 24 bit format image, the image consists of pixels consisting of 3 bytes, one for each red, green, and blue. The first byte in the image contains the blue color, the second byte contains the green color, and the third byte contains the red color of the pixel, and they form the color of the specified pixel.
Basic Layout of the Locked Array
Scan0 is the pointer to the 1st byte in the pixel array of the image which contains Height no. of rows and each row contains Stride no. of bytes in every row as shown in the diagram:
Each row contains the data of Width no. of pixels, where each pixel consists of PixelFormat no. of bytes. Thus total space taken by the Width no. of pixels is
Total space taken by Width no. Pixels = Width( no. of Pixels ) X PixelFormat( no. of bytes per pixel )
This space is aproximately equal to the Stride, but not exactly equal to it. In fact due to efficiency reasons, it is ensured that the Stride of an image contains a no. multiple of 4. For example, if an image is in 24 bits per pixel, and contains 25 pixel in each row, then it needs total space in each row equal to ( 75 = 3 X 25 ), but 75 is not the multiple of 4. Hence, the next multiple of 4 is used and in this case it is 76. Hence, 1 byte is remained unused as shown by the black area in every row in the above diagram. If space needed by the Width no. bytes is a multiple of 4, then the Stride is equal to it, and hence, there is no unused space mentioned as black area in the above diagram.
Iterating through the Image
First of all create the Bitmap object as:
Bitmap image = new Bitmap( "c:\images\image.gif" );
Then get the BitmapData object from it by calling the Lock method as:
BitmapData data = image.LockBits( new Rectangle( 0 , 0 , image.Width , image.Height ) , ImageLockMode.ReadWrite , PixelFormat.Format24bppRgb );
Then you can iterate in the image as:
unsafe { byte* imgPtr = ( byte* )( data.Scan0 ); for( int i = 0 ; i < data.Height ; i ++ ) { for( int j = 0 ; j < data.Width ; j ++ ) { // write the logic implementation here ptr += 3; } ptr += data.Stride - data.Width * 3; } }
Here unsafe, shows that you need to use the pointers in the unmanaged block, and the statement:
ptr += data.Stride - data.Width * 3;
shows that you need to skip the unused space.
Basic Difference Between Unsafe and GDI+
GDI is comparatively less efficient due to the overheads involved for Graphics adapters and windows messaging but working on image processing in unsafe mode provides extensively great performance parameters.