Reading Tips:
《Delphi Image ProcessingThe series focuses on efficiency. The general code is Pascal, and the core code is BaSm.
《C ++ Image ProcessingThe series focuses on code clarity and readability, and all uses C ++ code.
Make sure that the two items are consistent and can be compared with each other.
The code in this article must include the imagedata. Pas unit in "Delphi Image Processing-data type and public process.
USM sharpening is used to sharpen the image edge. It adjusts the contrast of the image edge details and generates a bright line and a dark line on both sides of the edge to make the image clearer.
USM sharpening is difficult to describe using formulas. The implementation steps are listed below:
1. Back up original image data;
2. Perform Gaussian blur on the Image Based on the given radius;
3. Subtract the original pixel from the Gaussian Blur pixel to form a difference;
4. Multiply the difference value by the quantity;
5. Divide the difference value into positive and negative parts and take the absolute value;
6. Subtract the given threshold values from the positive and negative differences;
7. The original pixel is added with the positive difference value minus the negative difference value, and the sharpening is completed.
The following is the USM sharpening code, including the Gaussian fuzzy code (for details about Gaussian blur, see the article 《Delphi Image Processing-Gaussian blur, The following Gaussian fuzzy code is also copied from this article ):
Procedure crossblur (var dest: timagedata; const Source: timagedata; weights: pointer; radius: integer); var height, srcstride: integer; dstoffset, srcoffset: integer; ASM push ESI push EDI push EBX push ECx mov ECx, [edX]. timagedata. stride mov srcstride, ECx call _ setcopyregs mov height, EDX mov srcoffset, eax mov dstoffset, EBX pop EBX pxor xmm7, xmm7 push ESI // PST = source. scan0 push EDI push edX Push ECx // blur Col mov eax, srcstride mov edX, eax SHR edX, 2 // width = source. width mov EDI, radius shl edi, 1 imul EDI, eax add EDI, ESI // PSB = PST + radius * 2 * Source. stride @ cyloop: Push edX @ cxloop: Push ESI push EDI push EBX mov ECx, radius pxor xmm0, xmm0 // sum = 0 cblurloop: movd xmm1, [esi] // for (I = 0; I <radius; I ++) movd xmm2, [EDI] // {punpcklbw xmm1, xmm7 punpcklbw XMM 2, xmm7 paddw xmm1, xmm2 // PS = PST + psb punpcklwd xmm1, xmm7 cvtdq2ps xmm1, xmm1 // PFS (flaot * 4) = ps (int * 4) mulps xmm1, [EBX] // PFS * = weights [I] addps xmm0, xmm1 // sum + = PFS add EBX, 16 Add ESI, eax // PST + = source. stride sub EDI, eax // PSB-= source. stride loop @ cblurloop //} movd xmm1, [esi] punpcklbw xmm1, xmm7 punpcklwd xmm1, xmm7 cvtdq2ps xmm1, xmm1 // PFS (flaot * 4) = PST (Int * 4) mulps xmm1, [EBX] // PFS * = weights [radius] addps xmm0, xmm1 // sum + = PFS pop EBX pop EDI pop ESI cvtps2dq xmm0, xmm0 // PS (int * 4) = sum (flaot * 4) packssdw xmm0, xmm7 packuswb xmm0, xmm7 movd [esi], xmm0 // PST (byte * 4) = ps (int * 4) pask add ESI, 4 Add EDI, 4 Dec edX jnz @ cxloop pop edX dec height jnz @ cyloop pop edX pop height pop EDI // Pd = DeST. scan0 pop ESI // PSL = PST MoV eax, radius SHL eax, 1 + 2 add eax, ESI // PSR = PSL + radius * 2 // blur row @ ryloop: Push edX // width = DeST. width @ rxloop: Push ESI push EBX push eax mov ECx, radius pxor xmm0, xmm0 // sum = 0 @ rblurloop: movd xmm1, [esi] // for (I = 0; I <radius; I ++) movd xmm2, [eax] // {punpcklbw xmm1, xmm7 punpcklbw xmm2, xmm7 paddw xmm1, xmm2 // PS = PSL + PSR punpcklwd xmm1, xmm7 cvtdq2ps xmm1, xmm1 // PFS (flaot * 4) = ps (int * 4) mulps xmm1, [EBX] // PFS * = weights [I] addps xmm0, xmm1 // sum + = PFS add EBX, 16 Add ESI, 4 // PSL ++ sub eax, 4 // PSR -- loop @ rblurloop //} movd xmm1, [esi] punpcklbw xmm1, xmm7 punpcklwd xmm1, xmm7 cvtdq2ps xmm1, xmm1 // PFS (flaot * 4) = PSL (int * 4) mulps xmm1, [EBX] // PFS * = weights [radius] addps xmm0, xmm1 // sum + = PFS cvtps2dq xmm0, xmm0 // PS (int * 4) = sum (flaot * 4) packssdw xmm0, xmm7 packuswb xmm0, xmm7 movd [EDI], xmm0 // Pd (byte * 4) = ps (int * 4) pask pop eax pop EBX pop ESI add eax, 4 Add ESI, 4 Add EDI, 4 Dec edX jnz @ rxloop add eax, srcoffset add ESI, srcoffset add EDI, dstoffset pop edX dec height jnz @ ryloop pop EBX pop EDI pop esiend; // --> st x // <-- st e ** x = 2 ** (x * log2 (E) function _ expon: extended; ASM fldl2e // y = x * Log2e fmul primary ST (0) // I = round (y) frndint fsub ST (1), St // F = Y-I fxch ST (1) // z = 2 ** F f2xm1 fld1 FADD fscale // result = z * 2 ** I fstp ST (1) end; function getweights (VAR buffer, weights: pointer; q: single; radius: integer): integer; const _ fcd1: single = 0.1; _ FC1: single = 1.0; _ FC2: single = 2.0; _ fc250: single = 250.0; fc_255: single = 255.0; var R: integer; V, qq2: Double; ASM mov R, ECx MoV ECx, eax unzip q fabs fcom _ fcd1 fstsw ax sahf Jae @ 1 then _ fcd1 fstp ST (1) // If (q <0.1) Q = 0.1 JMP @ 2 @ 1: fcom _ fc250 fstsw ax sahf jbe @ 2 then _ fc250 fstp ST (1) // If (q> 250) Q = 250 @ 2: fst q fmul _ FC2 fstp qq2 // qq2 = 2 * Q * q fwait mov eax, r test eax, eax JG @ 10 push eax // If (radius <= 0) fld1 // {fadd q // radius = ABS (q) + 1 fistp [esp]. integer fwait pop eax @ testrad IUS: // while (true) mov R, eax // {fldz // sum = 0 testloop: // For (r = radius; r> 0; r ++) fild R // {limit ST (0) fmulp ST (1), St fdiv qq2 fchs call _ expon // TMP = exp (-(R * r) /(2.0 * Q * q); cmp r, eax JNE @ 3 FST v // If (r = radius) V = TMP @ 3: faddp ST (1), ST (0) // sum + = TMP dec R jnz @ testloop //} fmul _ FC2 // sum * = 2 FADD _ FC1 // sum + = 1 fdivr v fmul _ fc255 fistp R cmp r, 0 je @ 4 // If (INT) (V/SUM * 255 + 0.5) = 0) break Inc eax // radius ++ JMP @ testradius //} @ 4: Dec eax jnz @ 5 Inc eax @ 5: mov R, eax //} @ 10: Inc eax SHL eax, 4 Add eax, 12 push edX push ECx mov edX, eax mov eax, ghnd call globalallocptr pop ECx pop edX test eax, eax JZ @ exit mov [ECx], eax // buffer = globalallocptr (ghnd, (radius + 1) * 16 + 12) add eax, 12 and eax, -16 mov [edX], eax // Wei Ghts = (char *) buffer + 12) & 0xfffffff0 mov ECx, r // ECx = radius mov edX, eax // edX = weights fldz // for (I = radius, sum = 0; I> 0; I --) @ clacloop: // {fild R limit ST (0) fmulp ST (1 ), st fdiv qq2 fchs call _ expon fstp [edX]. double // weights [I] = expon (-(I * I)/(2 * Q * q) FADD [edX]. double // sum + = weights [I] add edX, 16 dec R jnz @ clacloop //} fmul _ FC2 // sum * = 2 fld1 fstp [edX]. Double // weights [radius] = 1 FADD [edX]. double // sum + = weights [radius] Push ECx Inc ECx @ divloop: // for (I = 0; I <= radius; I ++) lead st (0) // weights [I] = round (weights [I]/SUM) fdivr [eax]. double FST [eax]. single FST [eax + 4]. single FST [eax + 8]. single fstp [eax + 12]. single add eax, 16 loop @ divloop Ffree ST (0) fwait pop eax/return radius @ Exit: end; // Gaussian blur. Procedure imagegaussiabblur (VAR data: timagedata; Q: single; radius: integer); var buffer, weights: pointer; SRC: timagedata; begin radius: = getweights (buffer, weights, Q, radius); If radius = 0 Then exit; if data. alphaflag then argbconvertpargb (data); SRC: = _ getexpanddata (data, radius); crossblur (data, SRC, weights, radius); freeimagedata (SRC); globalfreeptr (buffer ); if data. alphaflag then pargbco Nvertargb (data); end; Procedure dousmsharpen (var dest: timagedata; const Source: timagedata; amount512, threshold: integer); ASM push ESI push EDI push EBX pxor mm7, mm7 movd mm6, threshold movd MM5, ECx pshufw mm6, mm6, 11000000b pshufw MM5, MM5, 11000000b call _ setcopyregs @ yloop: Push ECx @ xloop: movd MM1, [esi] // mm0 = MM1 = original pixel movd mm2, [EDI] // mm2 = Gaussian Blur pixel movq mm0, MM1 punpcklbw MM1, mm7 Pu Npcklbw mm2, mm7 psubw MM1, mm2 psllw MM1, 7 pmulhw MM1, MM5 // MM1 = (original pixel-Gaussian Blur pixel) * quantity pxor mm2, mm2 psubw mm2, MM1 // mm2 =-MM1 psubw MM1, mm6 // MM1-= threshold psubw mm2, mm6 // mm2-= threshold packuswb MM1, mm7 // MM1 = positive deviation (0 for a value smaller than 0) packuswb mm2, mm7 // mm2 = negative deviation (0 for a value smaller than 0) paddusb mm0, MM1 psubusb mm0, mm2 // mm0 = original pixel + positive deviation-Negative Deviation movd [EDI], mm0 add ESI, 4 Add EDI, 4 loop @ xloop pop ECx add ESI, Eax add EDI, EBX dec edX jnz @ yloop pop EBX pop EDI pop ESI emmsend; // USM sharpen. Parameter: image data, radius, quantity, threshold value procedure imageusmsharpen (VAR data: timagedata; radius: single; amount: integer = 50; Threshold: integer = 0 ); procedure copydata (var dest: timagedata; const Source: timagedata); ASM push ESI push EDI mov ECx, [eax]. timagedata. width imul ECx, [eax]. timagedata. height mov EDI, [eax]. timagedata. scan0 mov ESI, [edX]. timagedata. scan0 rep movsd pop EDI pop ESI end; var isinvertscan0: Boolean; SRC: timagedata; begin if (amount <1) or (amount> 500) or (not (threshold in [0 .. 255]) Then exit; // back up image data SRC: = newimagedata (data. width, Data. height); isinvertscan0: = data. stride <0; If isinvertscan0 then _ invertscan0 (data); copydata (SRC, data); // use image data as Gaussian Blur imagegaussiabblur (data, radius, 0 ); // use the backup image data and Gaussian blur image data for USM sharpening. dousmsharpen (data, SRC, (amount SHL 9) Div 100, threshold); freeimagedata (SRC ); if isinvertscan0 then _ invertscan0 (data); end;
The following is a simple call example:
Procedure tform1.button2click (Sender: tobject); var BMP, BMP 2: tbitmap; data, data2: timagedata; begin // load image data from the file to BMP and bind it to data BMP: = tbitmap. create; BMP. loadfromfile ('d: \ source.bmp '); Data: = getbitmapdata (BMP); // USM sharpen: radius 3.0, quantity 150, threshold value 10 imageusmsharpen (data, 3,150, 10 ); // draw the image canvas after USM sharpening. draw (0, 0, BMP); // load Photoshop USM from the file to sharpen the image data to BMP 2, and bind it to data2 BMP 2: = tbitmap. create; BMP 2.loadfromfile ('d: \ sourceusm.bmp '); data2: = getbitmapdata (BMP 2); // compare imageusmsharpen sharpening process with Photoshop USM sharpening image data to imagecompare (data, data2 ); BMP 2.free; BMP. free; end;
The following is the running interface of the original image and the USM sharpening example in this article:
From the running interface, there is a certain error between USM sharpening and Photoshop USM sharpening in this article. This error is not large and is acceptable. However, it should be noted that the error here is the error produced by the two processing results of Gaussian blur and USM sharpen 《Delphi Image Processing-Gaussian blurThe implementation of Gaussian blur in this article is only similar to that of Photoshop Gaussian blur. Therefore, it is necessary to exclude the influence of Gaussian blur to determine the actual error between the USM sharpening algorithm and Photoshop in this article, the following example uses the Gaussian blur image processed by Photoshop to sharpen the USM image with the source image. The error will be completely produced by the USM sharpening code in this article:
Procedure tform1.button1click (Sender: tobject); var BMP, BMP 2: tbitmap; data, data2: timagedata; begin // load Photoshop Gaussian Blur (radius 3.0) image data from the file to BMP, and bind it to data BMP: = tbitmap. create; BMP. loadfromfile ('d: \ sourceblur.bmp '); Data: = getbitmapdata (BMP); // load image data from the file to BMP 2 and bind it to data2 BMP 2: = tbitmap. create; BMP 2.loadfromfile ('d: \ source.bmp '); data2: = getbitmapdata (BMP 2); // sharpen USM with Photoshop Gaussian blur image data and original data: 150, threshold Value 10 dousmsharpen (data, data2, (150 SHL 9) Div 100, 10); // draw the image canvas after USM sharpening. draw (0, 0, BMP); // load Photoshop USM from the file to sharpen the image data to BMP 2, and bind it to data2 BMP 2.loadfromfile ('d: \ sourceusm.bmp '); data2: = getbitmapdata (BMP 2); // compare the image data sharpening process of dousmsharpen with that of Photoshop USM to imagecompare (data, data2); BMP 2.free; BMP. free; end;
Running interface:
Running result, no error! Verify that the USM sharpening algorithm and steps in this document are correct.
In the previous two examples, image compare is called during image data comparison and is also listed below:
Procedure imagecompare (data1, data2: timagedata); var count, r_count, g_count, B _count: integer; diff, r_diff, g_diff, B _diff: integer; P1, P2: pargbquad; X, y, offset: integer; s: string; begin r_count: = 0; g_count: = 0; B _count: = 0; r_diff: = 0; g_diff: = 0; B _diff: = 0; P1: = data1.scan0; P2: = data2.scan0; offset: = data1.stride-data1.width * sizeof (targbquad); for Y: = 1 to data1.height do begin for X: = 1 to data1.width do begin DIFF: = p1 ^. red-P2 ^. red; If diff <> 0 then begininc (r_count); If diff <0 then DIFF: =-diff; If r_diff <diff then r_diff: = diff; end; DIFF: = p1 ^. green-P2 ^. green; If diff <> 0 then begininc (g_count); If diff <0 then DIFF: =-diff; If g_diff <diff then g_diff: = diff; end; DIFF: = p1 ^. blue-P2 ^. blue; If diff <> 0 then begininc (B _count); If diff <0 then DIFF: =-diff; If B _diff <diff then B _diff: = diff; end; INC (P1); Inc (P2); end; Inc (INTEGER (P1), offset); Inc (INTEGER (P2), offset); end; count: = data1.width * data1.height; s: = format ('total pixels: % d' + #13 + #10 + 'Red error count: % d, error rate: % d %, maximum error: % d' + #13 + #10 + 'green error count: % d, error rate: % d %, maximum error: % d' + #13 + #10 + 'Blue error count: % d, error rate: % d %, maximum error: % d', [count, r_count, (r_count * 100) Div count, r_diff, g_count, (g_count * 100) Div count, g_diff, B _count, (B _count * 100) Div count, B _diff]); showmessage (s); end;
For details about the use of GDI + units and descriptions in the "Delphi image processing" series, see the article 《GDI + for VCL basics-GDI + and VCL.
Due to limited levels, errors are inevitable. Correction and guidance are welcome. Email Address:Maozefa@hotmail.com
Here, you can access "Delphi Image Processing-Article Index".