Several common problems in GDI + (4)
5. Reading images is fast. How can this problem be solved or slow?
The bitmap class of GDI + provides two evil functions: getpixel and setpixel, which are used to obtain the color value of a certain pixel. The two functions can only be called once or twice. If I want to add the whole image to a red dot, use the followingCodeI guess you will not finish it until the daylily is cold. See how the following code is written.
1 Filestream FS = New Filestream (image, filemode. Open, fileaccess. Read );
2 Image img = Image. fromstream (FS, False , False );
3 Bitmap BMP = New Bitmap (IMG );
4 IMG. Dispose ();
5 FS. Close ();
6
7 For ( Int J = 0 ; J < BMP. height; j ++ )
8 {
9 For ( Int I = 0 ; I < BMP. width; I ++ )
10 {
11 Color color = BMP. getpixel (I, j );
12 Color = Color. fromargb (color. r + 20 , Color. G, color. B );
13 BMP. setpixel (I, j, color );
14 }
15 }
The Code logic is very clear. Lines 1st to 5th are well written. We used the methods in the previous sections to read images quickly without locking files. Of course, if you do not need to overwrite the original file, you do not need to copy it, and the speed will be faster. Next, we will make a loop on the image, and update the image data one row at a time. It is unknown that getpixel and setpixel are one of the most expensive functions in GDI. In addition, BMP. height and BMP. width is too slow. If you are processing a m pixel photo, you can have a cup of tea and get back after sleep.
Bitmap has a method called lockbits, which is to lock the memory area of the image according to the format and get the first address of the memory. In this way, you can directly rewrite the memory. The design of this method is quite good, but it is a pity that C ++ is the source. In. NET Framework, pointers are not recommended at all, and there is no pointer in VB. Finally, an intptr of chicken ribs is designed. This problem will be eliminated. C # In fact, it's okay. You can use unsafe code, or indirectly using pointers, VB. net. copy the content to a byte array, and then copy it back after processing. So the conclusion is that we need to use GDI + for image processing. We 'd better not use VB. NET, otherwise the memory will double.
Let's take a look at the quick writing method. Note that the unsafe switch is added during compilation to allow C # To use pointers:
1 Filestream FS = New Filestream (image, filemode. Open, fileaccess. Read );
2 Image img = Image. fromstream (FS, False , False );
3 Bitmap BMP = New Bitmap (IMG );
4 IMG. Dispose ();
5 FS. Close ();
6
7 Int Width = BMP. width;
8 Int Height = BMP. height;
9 Bitmapdata bmdata = BMP. lockbits ( New Rectangle ( 0 , 0 , Width, height), imagelockmode. readwrite, pixelformat. format24bpprgb );
10 Byte * P = ( Byte * ) Bmdata. scan0;
11 Int Offset = Bmdata. stride - Width * 3 ; // Only correct when pixelformat is format24bpprgb
12
13 For ( Int J = 0 ; J < Height; j ++ )
14 {
15 For ( Int I = 0 ; I < Width; I ++ )
16 {
17 P [ 2 ] + = 20 ; // Shocould check Boundary
18 P + = 3 ;
19 }
20 P + = Offset;
21 }
22
23 BMP. unlockbits (bmdata );
24 BMP. Dispose ();
Lines 1-5 are the same. Line 7 and 8 save a temporary variable. Do not call it every time, BMP. Width, BMP. Height.
Row 3 locks the image content to the system memory. This function has two reloads. The second type is complicated. It is used to lock the content in the user memory. Let's talk about this later. The first one, that is, what we use now, is to put the image content in the memory according to a certain format. Here we use format24bpprgb, which is a 24-bit color. In this format, three bytes represent a color, that is, the R, G, and B we usually know. Therefore, each byte represents a component of the color.
Row 10th uses a pointer pointing to the first address of the memory segment. In this way, we can directly modify the memory information of the image. Because each color component is a byte, the byte pointer is used. If format48bpprgb is used and each component uses two bytes, the short pointer should be used.
Line 1 needs to say two more words. stride refers to the number of bytes required for each row of the image. According to the BMP format standard, stride must be a multiple of 4. For example, for a 1024*768 24bpprgb image, the valid pixel information for each row should be 1024*3 = 3072. Because it is already a multiple of 4, stride is 3072. If the image is 35*30, the valid pixel information of a row is 105, but 105 is not a multiple of 4. Therefore, the stride should be 108 when the null byte is filled. The offset calculated by this row is 3. Here is another question. If it is a 16-color image, that is, a four-color image with a 50*50 image, what should be the stride?
Line 13-21 is the processing of loops. Note the following for Row 3. This sentence is actually wrong, because P is a byte, there is no symbol, the maximum value is 255, and adding 20 may overflow. If you do not want it to overflow, you can change it to row 1. If you want overflow to be the maximum value, you can use Row 2.
1 P [ 2 ] = Checked (( Byte ) (P [ 2 ] + 20 ));
2 P [ 2 ] = ( Byte ) Math. Min ( Byte . Maxvalue, P [ 2 ] + 20 );
In most cases, image processing uses Row 2. However, the performance loss is still very high, and a maximum value needs to be calculated, and another type of conversion is required, I am still studying how to make it faster. Another point is that the BMP image uses the storage method (little endian) on the decimal side, so the actual storage order of the image is G, B, R, here we need to increase the red weight of the image, which is the third byte instead of the first one.
The last two rows will not be mentioned. After processing, unlock is required and bitmap must be dispose.
The image processing speed with the above Code has been fast. The slowest part is to call the lockbits function, which is basically similar to that of GDI. However, if you still feel slow, there are two ways to improve the performance, but it is far beyond the scope of GDI +.
1. Do not use the GDI + method to read the image file. If it is BMP, it is easy to do. If it is JPG, Tif, PNG, GIF .... if you are good enough, you can write the FFT or DCT with your hand writing, I suggest you write it yourself, decompress the JPG file, modify it, and compress it back. If PNG/GIF 98 is the animated GIF, I will be speechless. The GIF standards are open and are not outdated. If you write it, you have to pay for it. So, forget it. You can read BMP by yourself to process the image very quickly without the need for lockbits.
2. If the image is super large and you use the next line 2 for processing, and you have extremely demanding performance requirements, there is still a way to go. The MMX/SSE/3D now instruction set is used directly, with four bytes simultaneously. Thank you for giving intel/AMD such a good instruction set. Unfortunately, you don't need to think about C #. You can't use it directly. Write a DLL using C ++ to call the MMX instruction set, and then call it using InterOP. There is also a loss in the InterOP process. The memory space of managed should be converted using the marshal in C. So if you really want to do this, I recommend using C ++. net, both of which can be written. Here I will not give an example of MMX/SSE. In the end, MMX is sufficient for image processing. SSE is mainly used for floating-point operations. If you have an image rendering, SSE is much faster, that is the content of graphics. I don't even understand it. I don't want to mention it. I'm afraid it will be hit by a stick.