I believe that everyone who has compiled programs in windows has used bitmaps more or less. Most people download some mature DIB class libraries from the Internet to use such as CxImage and CDIB ), A few people have a set of encapsulated DIB class libraries to facilitate future expansion and use. In recent years, GDI + has sprung up, providing unparalleled speed and quality in some processing aspects, such as scaling, rotation, and gradient filling. However, if you want to develop a complete image processing program, using it directly brings difficulties to the architecture design. You can encapsulate it in adapter mode before using it ).
What if you need some image processing operations? Many people without OO experienceC ++Programmers, for example, me a year ago, may do this: add methods directly to classes.
- Int FClamp0255 (int nValue) {return max (0, min (0xFF, nValue);} // saturated to 0--255
- Class FCObjImage
- {
- Public:
- Invert ();
- AdjustRGB (int R, int G, int B );
- };
- Void FCObjImage: Invert ()
- {
- If (GetHandle () = NULL) | (ColorBits () <24 ))
- Return;
- Int nSpan = ColorBits ()/8; // 3, 4 bytes per pixel
- For (int y = 0; y <Height (); y ++)
- {
- BYTE * pPixel = GetBits (y );
- For (int x = 0; x <Width (); x ++, pPixel + = nSpan)
- {
- PPixel [0] = ~ PPixel [0];
- PPixel [1] = ~ PPixel [1];
- PPixel [2] = ~ PPixel [2];
- }
- }
- }
- Void FCObjImage: AdjustRGB (int R, int G, int B)
- {
- If (GetHandle () = NULL) | (ColorBits () <24 ))
- Return;
- Int nSpan = ColorBits ()/8; // 3, 4 bytes per pixel
- For (int y = 0; y <Height (); y ++)
- {
- BYTE * pPixel = GetBits (y );
- For (int x = 0; x <Width (); x ++, pPixel + = nSpan)
- {
- PPixel [0] = FClamp0255 (pPixel [0] + B );
- PPixel [1] = FClamp0255 (pPixel [1] + G );
- PPixel [2] = FClamp0255 (pPixel [2] + R );
- }
- }
- }
Here are two examples to achieve reversed color and adjust the RGB value). In reality, there will be a lot of such operations: brightness, contrast, saturation ...... now let's look back at the steps for adding these methods: ooooooooooooo, RCP, my colleague's invention, full name: rapid copy paste ^-^ ), the first step is to copy a piece of code from the above, and then get rid of the interface and processing part. Although the Demo code here is very short and small, it won't be copied together with the bug, but there is another time bomb. One day, your boss told you: I can't stand waiting for a long time. Please add a progress bar to me ...... You may add a global variable and a parameter to each function, but it remains unchanged: You must modify the code of all these processing functions, cursing in your heart will not make you less change any of them. At this time, the bug has been waiting for the server... however, after a long time, your boss will let you add the regional processing function to it. After another month ......
Look back at the code again? That's right. Except for the red code, if the code is the same in other places, can we extract these algorithms separately? We may immediately think of the common callback methods in the standard library qsort and windows. Well, let's implement:
- Void Pixel_Invert (BYTE * pPixel)
- {
- PPixel [0] = ~ PPixel [0];
- PPixel [1] = ~ PPixel [1];
- PPixel [2] = ~ PPixel [2];
- }
- Void FCObjImage: PixelProcess (void (_ cdecl * PixelProc) (BYTE * pPixel ))
- {
- If (GetHandle () = NULL) | (ColorBits () <24 ))
- Return;
- Int nSpan = ColorBits ()/8; // 3, 4 bytes per pixel
- For (int y = 0; y <Height (); y ++)
- {
- BYTE * pPixel = GetBits (y );
- For (int x = 0; x <Width (); x ++, pPixel + = nSpan)
- {
- PixelProc (pPixel );
- }
- }
- }
- Void FCObjImage: Invert ()
- {
- PixelProcess (Pixel_Invert );
- }
Well, it looks good that the algorithm is stripped into a single function and we seem to have solved the problem. The processing of Invert is very good, but it is difficult to process AdjustRGB. How can we transmit the three adjustment parameters of RGB? There is only one interface parameter, by adding global variables/member variables? This is a method, but with the increase of class methods, the readability and maintainability of the program will decrease sharply, but it is not as good as the previous change.
How can we achieve high abstraction and good interfaces? We invite OOobject orient) to discuss its implementation. Design the following derivative relationships:
- class FCSinglePixelProcessBase
- {
- public :
- virtual void ProcessPixel (int x, int y, BYTE * pPixel) PURE ;
- } ;
- class FCPixelInvert : public FCSinglePixelProcessBase
- {
- public :
- virtual void ProcessPixel (int x, int y, BYTE * pPixel) ;
- } ;
- void FCPixelInvert::ProcessPixel (int x, int y, BYTE * pPixel)
- {
- pPixel[0] = ~pPixel[0] ; pPixel[1] = ~pPixel[1] ; pPixel[2] = ~pPixel[2] ;
- }
- class FCPixelAdjustRGB : public FCSinglePixelProcessBase
- {
- public :
- FCPixelAdjustRGB (int DeltaR, int DeltaG, int DeltaB) ;
- virtual void ProcessPixel (int x, int y, BYTE * pPixel) ;
- protected :
- int m_iDeltaR, m_iDeltaG, m_iDeltaB ;
- } ;
- void FCPixelAdjustRGB::ProcessPixel (int x, int y, BYTE * pPixel)
- {
- pPixel[0] = FClamp0255 (pPixel[0] + m_iDeltaB) ;
- pPixel[1] = FClamp0255 (pPixel[1] + m_iDeltaG) ;
- pPixel[2] = FClamp0255 (pPixel[2] + m_iDeltaR) ;
- }
Then we modify the image class as follows:
- # Include "PixelProcessor. h"
- Class FCObjImage
- {
- Public:
- Void PixelHandler (FCSinglePixelProcessBase & PixelProcessor, FCObjProgress * progress = NULL );
- };
- Void FCObjImage: PixelHandler (FCSinglePixelProcessBase & PixelProcessor, FCObjProgress * progress)
- {
- If (GetHandle () = NULL)
- Return;
- Int nSpan = ColorBits ()/8; // 3, 4 bytes per pixel
- For (int y = 0; y <Height (); y ++)
- {
- BYTE * pPixel = GetBits (y );
- For (int x = 0; x <Width (); x ++, pPixel + = nSpan)
- {
- PixelProcessor. ProcessPixel (x, y, pPixel );
- }
- If (progress! = NULL)
- Progress-> SetProgress (y * 100/Height ());
- }
- }
- Void FCObjImage: Invert (FCObjProgress * progress)
- {
- PixelHandler (FCPixelInvert (), progress );
- }
- Void FCObjImage: AdjustRGB (int R, int G, int B, FCObjProgress * progress)
- {
- PixelHandler (FCPixelAdjustRGB (R, G, B), progress );
- }
The above is just a basic framework. You can easily add the region processing parameters-pass a RECT parameter during construction .)
An object is really a wonderful thing. It can provide a simple interface, and it can encapsulate a lot of additional information.
Well, now let's test the result: add an operation to black the odd lines of the image and white the even lines.
- Class FCPixelTest: public FCSinglePixelProcessBase
- {
- Public:
- Virtual void ProcessPixel (int x, int y, BYTE * pPixel );
- };
- Void FCPixelTest: ProcessPixel (int x, int y, BYTE * pPixel)
- {
- If (y % 2) pPixel [0] = pPixel [1] = pPixel [2] = 0;
- // Odd number of rows
- Else
- PPixel [0] = pPixel [1] = pPixel [2] = 0xFF;
- // Even rows
- }
Then make the following call:
- PixelHandler (FCPixelTest(), progress) ;
How harmonious and beautiful the algorithm design personnel only need to write their own algorithms, instead of considering how to make it support progress bars and regions. This is like a well-designed AK. You can constantly add bullet objects to it.) ^-^
So far, we have achieved success. Is there any problem?
Wait, don't be busy. It's not right in some places. After I add this algorithm, how can I compile it for so long.
The problem lies in the inconspicuous one:
- #include "PixelProcessor.h"
Image is the underlying object of image processing. All files in the project contain it directly or indirectly. Therefore. h itself and its contained. modifications to h will cause almost the entire project build, which of course cannot be tolerated. The solution is to use the "pre-declaration ", because in the PixelHandler interface, we only need to reference it, that is, the "My interface") and do not need to know the internal structure of the class passed to me. Give me a 32 (64) ).
Therefore
- #include "PixelProcessor.h"
Replace:
- Class FCSinglePixelProcessBase; // external class pre-declaration
Then, the. cpp file contains PixelProcessor. h. In this way, the change to PixelProcessor. h will only cause the. cpp file to be re-compiled, greatly saving the Compilation Time.
Summary:
1) If possible, never think about the word "Copy code" in programming. After all, OO was born for abstraction and code reuse.
2) Unless necessary, the member variables and function parameters of the class should be replaced by pointers or references as much as possible. h contains as few as possible. h file, instead of pre-declaration, to reduce the Compilation Time and possible cross-inclusion in the future.
3) Finally, let's talk about efficiency: Some friends may say that calling virtual functions in each pixel will affect the performance. This is true, but the actual loss is far from the expectation. I tested it for a moment: I used anti-chip processing for 1024*768 images, with a speed of about 5% loss. When I processed the brightness, contrast, and gamma, the loss could be completely ignored, after all, the code that comes out of the stack is only used to access the stack and look up the table, rather than the time-consuming commands such as floating point division.