In. NET programming, due to the emergence of GDI +, the image processing function is greatly enhanced. This article introduces the common image processing methods and principles in. NET through a simple black/white processing instance and compares the performance of various methods.
Black and white processing principle: there are usually three algorithms for processing color images into black and white effects;
(1). Maximum Value Method: Make the R, G, and B values of each pixel equal to the largest one in the RGB (color value) of the original pixel;
(2). Average Method: The R, G, and B values of each pixel are equal to the average values of the RGB values of the original pixel;
(3). weighted average method: weighted the R, G, and B values of each pixel.
I think that the black and white images produced by the third method are "authentic ".
1. GetPixel Method
GetPixel (I, j) and SetPixel (I, j, Color) can directly obtain the Color structure of an image pixel, but the processing speed is relatively slow.
/// <Summary>
/// Pixel Method
/// </Summary>
/// <Param name = "curBitmap"> </param>
Private void PixelFun (Bitmap curBitmap)
{
Int width = curBitmap. Width;
Int height = curBitmap. Height;
For (int I = 0; I <width; I ++) // If I <curBitmap. Width is used as a loop, the performance will be affected.
{
For (int j = 0; j {
Color curColor = curBitmap. GetPixel (I, j );
Int ret = (int) (curColor. R * 0.299 + curColor. G * 0.587 + curColor. B * 0.114 );
CurBitmap. SetPixel (I, j, Color. FromArgb (ret, ret, ret ));
}
}
}
I <curBitmap. width is used as a loop condition, but should be retrieved and saved to a variable. In this way, the Width attribute is not retrieved from curBitmp every time during the loop, thus improving the performance.
However, the direct pixel extraction method cannot process large pixel images. It takes 2182ms to process a 1440*900 image:
Before processing:
After processing:
We can intuitively see that the time used for multiple tests has a slight fluctuation.
2. memory copy method
The memory copy method uses System. runtime. interopServices. marshal. copy Copies the image data to the array and then processes it. You do not need to directly operate the pointer without using unsafe. The processing speed is not much different from the pointer processing speed, processing a pair of 1440*900 images takes about 34 ms.
A class required for both the memory copy and pointer Methods: BitmapData
BitmapData class
The BitmapData object specifies the properties of the bitmap.
1. Height attribute: the Height of the locked bitmap.
2. Width attribute: the height of the locked bitmap.
3. PixelFormat attribute: the actual pixel format of the data.
4. Scan0 attribute: the first byte address of the locked array. If the entire image is locked, it is the first byte address of the image.
5. Stride attribute: Stride, also known as scan width.
As shown in, the length of the array is not necessarily equal to the length of the pixel array of the image. There are still some unused areas. This involves the bitmap data structure. The system must ensure that the number of bytes per line must be a multiple of 4.
Assume that the width of an image is 6, because the format is Format24bppRgb (3 bytes per pixel.Unless otherwise specifiedBitmapAre considered as 24Bit RGB)OfObviously, each row needs 6*3 = 18 bytes of storage. This is true for Bitmap. However, for BitmapData, although BitmapData. width is equal to Bitmap. width, but probably because of the display performance, the actual number of bytes in each line will be an integer greater than or equal to the 4 nearest to it, the actual number of bytes is Stride. In this example, 18 is not an integer multiple of 4, and the multiples of 4 closest to 18 are 20, so BitmapData. Stride = 20. Obviously, when the Width itself is a multiple of 4, BitmapData. Stride = Bitmap. Width * 3.
It may be better to draw a picture (this figure only applies when PixelFormat = PixelFormat. Format24bppRgb, each pixel occupies 24 bytes in total ). R, G, and B represent three primary color component bytes, respectively. BGR represents one pixel. To make it look convenient, I inserted a space between each pixel, which is actually none. X indicates the number of automatically inserted bytes that multiply by 4. In order to meet the reading habits of humans, I am a branch. In fact, it should be regarded as a continuous segment in the computer memory.
Scan0
|
| ------- Stride --------- |
| ------- Width --------- |
BGR XX
BGR XX
BGR XX
.
The Format24bppRgb format meets the following requirements:
BitmapData. Width * 3 +No space for each row (XX) = BitmapData. Stride
Similarly, for Format32bppRgb or Format32bppPArgb:
BitmapData. Width * 4 +No space for each row (XX) = BitmapData. Stride
/// <Summary>
/// Memory copy method
/// </Summary>
/// <Param name = "curBitmap"> </param>
Private unsafe void MemoryCopy (Bitmap curBitmap)
{
Int width = curBitmap. Width;
Int height = curBitmap. Height;
Rectangle rect = new Rectangle (0, 0, curBitmap. Width, curBitmap. Height );
System. Drawing. Imaging. BitmapData bmp data = curBitmap. LockBits (rect, System. Drawing. Imaging. ImageLockMode. ReadWrite, PixelFormat. Format24bppRgb); // curBitmap. PixelFormat
IntPtr = BMP data. Scan0;
Int bytesCount = BMP data. Stride * BMP data. Height;
Byte [] arrDst = new byte [bytesCount];
Marshal. Copy (ptr, arrDst, 0, bytesCount );
For (int I = 0; I <bytesCount; I + = 3)
{
Byte colorTemp = (byte) (arrDst [I + 2] * 0.299 + arrDst [I + 1] * 0.587 + arrDst [I] * 0.114 );
ArrDst [I] = arrDst [I + 1] = arrDst [I + 2] = (byte) colorTemp;
}
Marshal. Copy (arrDst, 0, ptr, bytesCount );
CurBitmap. UnlockBits (BMP data );
}
3.Pointer Method
The pointer is an unsafe operation in c # And must be enclosed by unsafe for processing. The fastest speed is. It takes about 18 ms to process a 180*180 image.
Byte * ptr = (byte *) (BMP data. scan0); obtain the pointer to the root position of the image data, and then use BMP data. scan 0 to obtain the scanning width of the image, and then you can perform pointer operations.
/// <Summary>
/// Pointer Method
/// </Summary>
/// <Param name = "curBitmap"> </param>
Private unsafe void PointerFun (Bitmap curBitmap)
{
Int width = curBitmap. Width;
Int height = curBitmap. Height;
Rectangle rect = new Rectangle (0, 0, curBitmap. Width, curBitmap. Height );
System. Drawing. Imaging. BitmapData bmp data = curBitmap. LockBits (rect, System. Drawing. Imaging. ImageLockMode. ReadWrite, PixelFormat. Format24bppRgb); // curBitmap. PixelFormat
Byte temp = 0;
Int w = BMP data. Width;
Int h = BMP data. Height;
Byte * ptr = (byte *) (BMP data. Scan0 );
For (int I = 0; I {
For (int j = 0; j <w; j ++)
{
Temp = (byte) (0.299 * ptr [2] + 0.587 * ptr [1] + 0.114 * ptr [0]);
Ptr [0] = ptr [1] = ptr [2] = temp;
Ptr + = 3; // Format24bppRgb format each pixel occupies 3 bytes
}
Ptr + = BMP data. Stride-BMP data. Width * 3; // when each row reads the last "useful" data, skip unused space XX
}
CurBitmap. UnlockBits (BMP data );
}
The following are multiple groups of test data:
|
1920*1080 |
1440*900 |
1208*800 |
1024*768 |
500*544 |
200*169 |
Direct pixel Extraction |
1705ms |
1051ms |
1710ms |
1340ms |
450ms |
32ms |
Memory copy method |
54ms |
33ms |
26ms |
20ms |
7ms |
0ms |
Pointer Method |
28ms |
17ms |
14ms |
10ms |
3ms |
0ms |
It can be seen that the efficiency of pointer and direct pixel extraction is two orders of magnitude!
Compare the advantages and disadvantages of the above method: 1. in general, the performance pointer method is slightly better than the memory copy method, and the direct pixel extraction method has the lowest performance. 2. the performance of big image processing pointer and memory copy methods has been significantly improved, and small images are relatively fast. 3. direct pixel extraction is easy to use and requires no attention to the image pixel format (PixelFormat), which is a secure code; if the memory copy method and pointer method do not change the pixel format of the original image, different processing needs to be performed for different pixel formats, which is unsafe code.