Reading Tips:
The C ++ image processing series focuses on code clarity and readability, all using C ++ code.
《Delphi Image ProcessingThe series focuses on efficiency. The general code is Pascal, and the core code is BaSm.
Make sure that the two items are consistent and can be compared with each other.
The code in this article must include "C ++ Image Processing-data types and common functions"The header file of BMP data. h in this article.
Photoshop provides a rich set of Image Layer blending modes. The color blending mode is to mix the brightness of the layer image with the layer filled color or image color, the gray level of the lower-layer image is retained. Based on this hybrid feature, the color mixing mode is often used to color the grayscale image.
It has been a hot topic for programmers to learn how to use program code to accurately implement the color mixing mode of Photoshop layers. This article uses tools such as bcb2007 and GDI + to better implement its main functions (excluding opacity and fill options ).
According to Photoshop, the color mixing mode is a mixture of the color phases, saturation, and brightness of the layer image pixels. In this way, in the program code, we need to first extract the color phases, saturation, and brightness (brightness) of the Upper-layer image color, the color and saturation are extracted by HSV, and then calculated and synthesized by Pixel based on the color brightness of the layer according to the ratio of 0.3r + 0.59G + 0.11b. In fact, in the process of color synthesis, no matter whether it is HSV, HSL or other HSB methods, it cannot achieve the expected effect. For example, the upper color r = 225, G = 211, B = 179, the extracted h and S are 179, respectively, and the lower gray level is. The color G is synthesized using HSV or SHL, B is 0, and the R, G, and B of the actual synthesis should be 192,178,146 respectively.
Through repeated experiments in Photoshop, it is found that the saturation in the upper color does not seem to play any role in the synthesis process. The final synthesis result is as long as the ratio of the upper color to the lower gray level remains unchanged, this is also a necessary condition for the color mixing mode. The gray scale ratio must be ensured. If the two are in conflict, do not consider the color proportion (for example, the gray scale of a pixel is 0 or 255 ). According to this idea, I gave up the HSB method for synthesis, and used the method for solving the equation according to the above two conditions to achieve color mixing. To do this, you can list the following equality relationships:
1: max-min =
2: Mid-min = B
. 3R + 0.59G + 0.11b = C
Max, mid, and min are the maximum, middle, and minimum values in the upper-layer colors R, G, and B respectively. Equations 1 and 2 represent the proportions of upper-layer colors, while Equation 3 represents the gray proportions of lower-layer colors.
If we only consider the color phases within 60 degrees and assume r> G> B, the above three equations can be listed as the three-element one-time equations below:
1) r-B =
2) g-B = B
3) 0.3r + 0.59G + 0.11b = C
The result is correct when the constant of any color that meets the color range of 0-60 and r> G> B is substituted into the above equations. However, the actual color mixing uses two different gray scales and colors. The RGB values obtained using the above equations may exceed the range of 0-255, however, we cannot add this range restriction to the equations. Therefore, we must adjust the RGB values out of the range in the program code.
The color mixing mode of Photoshop layers uses the Black and White adjustment method when extracting the brightness of the lower layer image. It is actually a gray computing method, but the gray computing method of the same general pixel is different. Calculation formula:
Bwgray = (max-min) * ratio_max + (mid-min) * ratio_max_mid + min
In the formula, bwgray is black and white gray; max, mid, and min are the maximum, middle, and minimum values of each RGB component, respectively; ratio_max is the color (monochrome) ratio represented by Max in pixels, ratio_max_mid is the relationship between Max and mid in pixels.
In this formula, the monochrome and intercolors are the concept in Photoshop, and those who are not familiar with Photoshop may not be easy to understand. The following is an example:
If the R, G, and B values of a certain pixel are 200, 100, and 50, max = r = 200, mid = G = 100, min = B = 50, obviously, ratio_max is the ratio of R (red), while ratio_max_mid is the ratio of R (red) to the color (yellow) formed by G (green, based on the formula above and the default ratio adjusted by Photoshop black/white (Red = 40%, yellow = 60%), calculate the black/white gray value of the pixel, then:
Bwgray = (200-100) * 0.4 + (100-50) * 0.6 + 50 = 120
According to this principle, in the Photoshop black and white function adjustment dialog box, only adjusting the ratio of red to yellow will take effect on the pixel gray value in the example.
Below is all the program code I wrote based on the above ideas and formulas:
// Define typedef floatbwparams, * pbwparams; // adjust the default parameters in black/white mode: red, yellow, green, foreign red, blue, and blue const int _ bwdefault [] = {410,614,410,819,205,614 }; enum {bwindexblue = 0x40000, bwindexgreen = 0x20000, bwindexred = 0x00000}; Enum {indexblue = 0x00000, indexgreen = 0x10000, indexred = 0x20000}; typedef Union // color component switching Structure {int TMP; // Temporary Variable struct {short value used for switching; // color component value short Index; // color component index };} rgbindex; // transpose // The forceinlinevoid swaprgb (rgbindex & A, rgbindex & B) {. TMP ^ = B. TMP; B. TMP ^ =. TMP;. TMP ^ = B. TMP;} // obtain the grayscale forceinlineintgetbwgray (const pargbquad pixel, const pint bwparams) {rgbindex Max, mid, min; Min. TMP = pix El-> Blue | bwindexblue; mid. TMP = pixel-> green | bwindexgreen; max. TMP = pixel-> Red | bwindexred; If (max. value <mid. value) swaprgb (max, mid); If (max. value <min. value) swaprgb (max, min); If (min. value> mid. value) swaprgb (Min, mid); Return (max. value-mid. value) * bwparams [Max. index] + (MID. value-min. value) * bwparams [Max. index + mid. index-1] + 512)> 10) + min. value ;}//--------------------------- ------------------------------------------------ Void colormix (pargbquad PD, const pargbquad ps, int gray) {// gray computing constant: blue, green, red const double ys [] = {0.11, 0.59, 0.30 }; rgbindex Max, mid, min; Min. TMP = ps-> Blue | indexblue; mid. TMP = ps-> green | indexgreen; max. TMP = ps-> Red | indexred; If (max. value <mid. value) swaprgb (max, mid); If (max. value <min. value) swaprgb (max, min); If (min. value> mid. value) swaprgb (MI N, mid); int max_min = max. value-min. value; // If the saturation is 0, return the grayscale if (max_min = 0) {Pd-> Blue = Pd-> Green = Pd-> Red = gray; return ;} int mid_min = mid. value-min. value; double huecoef = (double) mid_min/(double) max_min; // assume that the maximum value is R, the center value is g, and the minimum value is B. Set the equations: // 1 ): -B + R = max-min // 2):-B + G = mid-min // 3 ): 11B + 59G + 30r = gray * 100int e1 [4], E2 [4], e3 [4], e4 [4], E5 [4], E6 [4]; e1 [Max. index] = 1; e1 [mid. index] = 0; e1 [Min. index] =-1; e1 [3] = max_min; E2 [Max. index] = 0; E2 [mid. index] = 1; E2 [Min. index] =-1; E2 [3] = mid_min; e3 [0] = 11; e3 [1] = 59; e3 [2] = 30; e3 [3] = gray * 100; // equations: // 4): (1)-2) * 30 // 5): 2) * 11 // 6): 3)-4) + 5) for (INT I = 0; I <4; I ++) {e4 [I] = (e1 [I]-E2 [I]) * e3 [Max. index]; E5 [I] = e2 [I] * e3 [Min. index]; E6 [I] = e3 [I]-e4 [I] + E5 [I];} int newmax; // calculate g solution: 6) /100 (due to the gray formula, the right side of the equation is always 100) Int newmid = (E6 [3] + 50)/100; // evaluate B solution: G substituted 2) int newmin = newmid-E2 [3]; // If B <0, B = 0, and returns the R, G, and equation 1 to 1 based on the ratio of the gray scale to the color phase ): 0.3r + 0.59G = gray // 1-2): huecoef * r-g = 0if (newmin <0 | newmid <= 0) {newmax = (INT) (gray/(YS [Max. index] + ys [mid. index] * huecoef) + 0.5); newmid = (INT) (newmax * huecoef + 0.5); newmin = 1;} // otherwise, evaluate the R solution: G and B are substituted into 1) else {newmax = newmin + e1 [3]; // If R> 255, r = 255, at the same time according to the gray scale ratio Returns the G, B, and equation of the proportional solution of the color phase: 2-1): 0.59G + 0.11b = gray-0.3*255/2-2 ): G + (huecoef-1) B = 255 * huecoefif (newmax> 255) {newmin = (INT) (Gray-(YS [Max. index] + ys [mid. index] * huecoef) * 255)/(YS [Min. index]-Ys [mid. index] * (huecoef-1) + 1.0); newmid = (INT) (newmin + (255-newmin) * huecoef + 0.5); newmax = 255 ;}} (lpbyte) PD) [Max. index] = newmax> 255? 255: newmax; (lpbyte) PD) [Mid. Index] = newmid> 255? 255: newmid; (lpbyte) PD) [min. Index] = newmin> 255? 255: newmin;} // ----------------------------------------------------------------------------- // adjust the image black and white. // Adjust the bwparams parameter to be an array pointer with the element number equal to 6, which is respectively red, yellow, green, blue, and foreign red void imageblackwhite (bitmapdata * data, const pbwparams bwparams = NULL) {// copy the pixel grayscale parameter and exchange the blue and foreign red int Params [6], * pparams; If (bwparams) {for (INT I = 0; I <6; I ++) Params [I] = (INT) (bwparams [I] * 1024 + 0.5); Params [3] ^ = Params [5]; params [5] ^ = Params [3]; Params [3] ^ = Params [5]; pparams = Params;} elsepparams = (int *) _ bwdefault; pargbquad P = (pargbquad) Data-> scan0; Int dataoffset = (data-> stride> 2)-(INT) Data-> width; For (uint y = 0; y <data-> height; y ++, P + = dataoffset) {for (uint x = 0; x <data-> width; X ++, P ++) {int gray = getbwgray (p, pparams ); p-> Blue = p-> Green = p-> Red = (Gray &~ 0xff) = 0? Gray: Gray> 255? 255: 0; }}// ------------------------------------------------------------------------------- // grayscale image dyeing. Void imagetint (bitmapdata * graydata, argb color) {pargbquad P = (pargbquad) graydata-> scan0; int dataoffset = (graydata-> stride> 2)-(INT) graydata-> width; For (uint y = 0; y <graydata-> height; y ++, P + = dataoffset) {for (uint x = 0; x <graydata-> width; X ++, P ++) {colormix (p, (pargbquad) & color, p-> blue );}}} // combine // void imagecolormixer (bitmapdata * DEST, const bitmapdata * Source) {pargbquad PD, PS; uint width, height; int dstoffset, srcoffset; getdatacopyparams (DEST, source, width, height, PD, PS, dstoffset, srcoffset); For (uint y = 0; y
In the above Code, the colormix function writes a detailed code for solving the equation process, and makes corresponding annotations. When solving a three-element system, the gray scale value is increased by 100 times, you can use the Count calculation. There is no error in the calculation process because the gray scale relationship is always equal to 100; two groups of binary one-time equations are used for processing the RGB values that exceed the range 0-255. In addition, an rgbindex type is defined, this saves the original R, G, and B information during the comparison and exchange of the maximum and minimum values, which not only facilitates the calculation in the code, in addition, the applicability of the preceding three-element system is extended from 60 degrees above the color phase and r> G> B to the full range of the color phase and R, G, and B values of any size, it also avoids the switch condition statement that is commonly used when HSB is converted to RGB.
In order to check whether the ideas and codes in this article are correct, after the code in this article is adjusted in black and white, grayscale image dyeing, and image color mixing, the images produced by the same parameter adjustment with Photoshop are compared by pixel, below is a simple comparison function code:
// Define void imagecompare (Bitmap * BMP 1, bitmap * BMP 2) {int count, r_count = 0, g_count = 0, B _count = 0; int diff, r_diff = 0, g_diff = 0, B _diff = 0; bitmapdata data1, data2; gdiplus: rect R (0, 0, BMP 1-> getwidth (), BMP 1-> getheight ()); BMP 1-> lockbits (& R, region, pixelformat24bpprgb, & data1); BMP 2-> lockbits (& R, region, pixelformat24bpprgb, & data2); try {prgbtriple p1 = (prgbtriple) data1.scan0; prgbtriple P2 = (prgbtriple) data2.scan0; int offset = data1.stride-data1.width * sizeof (rgbtriple); For (unsigned y = 0; y <data1.height; y ++, (char *) P1 + = offset, (char *) P2 + = offset) {for (unsigned x = 0; x <data1.width; X ++, P1 ++, p2 ++) {diff = p1-> rgbtred-P2-> rgbtred; If (diff) {r_count ++; If (diff <0) diff =-diff; if (r_diff <diff) r_diff = diff;} diff = p1-> rgbtgreen-P2-> rgbtgreen; If (diff) {g_count ++; If (diff <0) diff =-diff; If (g_diff <diff) g_diff = diff;} diff = p1-> rgbtblue-P2-> rgbtblue; If (diff) {B _count ++; if (diff <0) diff =-diff; If (B _diff <diff) B _diff = diff ;}}__ finally {BMP 2-> unlockbits (& data2 ); BMP 1-> unlockbits (& data1);} COUNT = data1.width * data1.height; string s; S. sprintf (L "Total pixels: % d \ n" \ L "Red error count: % d, error rate: % d %, maximum error: % d \ n "\ L" Green error count: % d, error rate: % d %, maximum error: % d \ n "\ L" Blue error count: % d, error Rate: % d %, maximum error: % d ", Count, r_count, (r_count * 100)/count, r_diff, g_count, (g_count * 100)/count, g_diff, B _count, (B _count * 100)/count, B _diff); showmessage (s );}//---------------------------------------------------------------------------
First, adjust the image in black and white. See the following example:
//---------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender){Gdiplus::Bitmap *bmp = new Gdiplus::Bitmap(L"..\\..\\media\\Source.bmp");BitmapData data;LockBitmap(bmp, &data);ImageBlackWhite(&data);UnlockBitmap(bmp, &data);Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);g->DrawImage(bmp, 0, 0);delete g;Gdiplus::Bitmap *bmp2 = new Gdiplus::Bitmap(L"..\\..\\media\\GraySource.bmp");ImageCompare(bmp, bmp2);delete bmp2;delete bmp;}//---------------------------------------------------------------------------
The original image is shown below:
Black and white Adjustment Example program running interface, the error after comparison is 0:
Here is an example of image dyeing:
//---------------------------------------------------------------------------void __fastcall TForm1::Button2Click(TObject *Sender){Gdiplus::Bitmap *bmp = new Gdiplus::Bitmap(L"..\\..\\media\\Source.bmp");BitmapData data;LockBitmap(bmp, &data);ImageBlackWhite(&data);ImageTint(&data, 0xff314ead);UnlockBitmap(bmp, &data);Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);g->DrawImage(bmp, 0, 0);delete g;Gdiplus::Bitmap *bmp2 = new Gdiplus::Bitmap(L"..\\..\\media\\Source314ead.bmp");ImageCompare(bmp, bmp2);delete bmp2;delete bmp;}//---------------------------------------------------------------------------
The running result is as follows:
The dyeing error is 1.
Finally, the source image above is the background (lower-layer image), and the color mode is mixed with a landscape image (upper-layer image:
//---------------------------------------------------------------------------void __fastcall TForm1::Button3Click(TObject *Sender){Gdiplus::Bitmap *bmp = new Gdiplus::Bitmap(L"..\\..\\media\\Source.bmp");Gdiplus::Bitmap *bmp1 = new Gdiplus::Bitmap(L"..\\..\\media\\Test1.bmp");BitmapData dest, source;LockBitmap(bmp, &dest);LockBitmap(bmp1, &source);ImageColorMixer(&dest, &source);UnlockBitmap(bmp1, &source);UnlockBitmap(bmp, &dest);delete bmp1;Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);g->DrawImage(bmp, 0, 0);delete g;Gdiplus::Bitmap *bmp2 = new Gdiplus::Bitmap(L"..\\..\\media\\TestMix.bmp");ImageCompare(bmp, bmp2);delete bmp2;delete bmp;}//---------------------------------------------------------------------------
Foreground image:
Running result diagram:
The error is 2, and the error rate is not high.
In general, the feasibility of the Code in this article is relatively high.
Due to limited levels, errors are inevitable. Correction and guidance are welcome. Email Address:Maozefa@hotmail.com
Here, you can access "C ++ Image Processing-Article Index".