Delphi图像处理 -- USM锐化

页面导航:首页 > 软件编程 > Delphi > Delphi图像处理 -- USM锐化

Delphi图像处理 -- USM锐化

来源: 作者: 时间:2016-01-18 15:41 【

USM锐化是用来锐化图像边缘的,它通过调整图像边缘细节的对比度,并在边缘的两侧生成一条亮线一条暗线,使画面整体更加清晰。 USM锐化用公式描述很麻烦,这里干脆实现步骤列于下
 
 
    USM锐化是用来锐化图像边缘的,它通过调整图像边缘细节的对比度,并在边缘的两侧生成一条亮线一条暗线,使画面整体更加清晰。
 
    USM锐化用公式描述很麻烦,这里干脆实现步骤列于下面:
 
    1、备份图像原数据;
 
    2、按给定半径对图像进行高斯模糊;
 
    3、用原像素与高斯模糊后的像素相减,形成一个差值;
 
    4、将差值乘以数量;
 
    5、将差值分为正负2部分,取绝对值;
 
    6、正负差值分别减去给定的阈值;
 
    7、原像素加上正差值减去负差值,锐化完毕。
 
    下面是USM锐化代码,包括高斯模糊代码(关于高斯模糊见文章《Delphi图像处理 -- 高斯模糊》,下面的高斯模糊代码也是从该文拷贝来的):
 
[]  
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 xmm2, 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       esi  
end;  
  
// --> st x   
// <-- st e**x = 2**(x*log2(e))   
function _Expon: Extended;  
asm  
    fldl2e              // y = x*log2e   
    fmul  
    fld     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;  
  _fc255: Single = 255.0;  
var  
  R: Integer;  
  v, QQ2: double;  
asm  
    mov     R, ecx  
    mov     ecx, eax  
    fld     Q  
    fabs  
    fcom    _fcd1  
    fstsw   ax  
    sahf  
    jae     @@1  
    fld     _fcd1  
    fstp    st(1)               // if (Q < 0.1) Q = 0.1   
    jmp     @@2  
@@1:  
    fcom    _fc250  
    fstsw   ax  
    sahf  
    jbe     @@2  
    fld     _fc250  
    fstp    st(1)               // if (Q > 250) Q = 250   
@@2:  
    fst     Q  
    fmul    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  
@@testRadius:                   //   while (TRUE)   
    mov     R, eax              //   {   
    fldz                        //     sum = 0   
@@testLoop:                     //     for (R = radius; R > 0; R ++)   
    fild    R                   //     {   
    fld     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          // weights = ((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  
    fld     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 ++)   
    fld     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;  
  
// 高斯模糊。   
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   
//    PArgbConvertArgb(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 = 原像素   
    movd      mm2, [edi]  // mm2 = 高斯模糊像素   
    movq      mm0, mm1  
    punpcklbw mm1, mm7  
    punpcklbw mm2, mm7  
    psubw     mm1, mm2  
    psllw     mm1, 7  
    pmulhw    mm1, mm5    // mm1 = (原像素 - 高斯模糊像素) * 数量   
    pxor      mm2, mm2  
    psubw     mm2, mm1    // mm2 = -mm1   
    psubw     mm1, mm6    // mm1 -= 阈值   
    psubw     mm2, mm6    // mm2 -= 阈值   
    packuswb  mm1, mm7    // mm1 = 正离差(小于0的值饱和为0)   
    packuswb  mm2, mm7    // mm2 = 负离差(小于0的值饱和为0)   
    paddusb   mm0, mm1  
    psubusb   mm0, mm2    // mm0 = 原像素 + 正离差 - 负离差   
    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  
    emms  
end;  
  
// USM锐化。参数:图像数据,半径,数量,阈值   
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;  
  // 备份图像数据   
  Src := NewImageData(Data.Width, Data.Height);  
  IsInvertScan0 := Data.Stride < 0;  
  if IsInvertScan0 then  
    _InvertScan0(Data);  
  CopyData(Src, Data);  
  // 图像数据作高斯模糊   
  ImageGaussiabBlur(Data, Radius, 0);  
  // 用备份图像数据和高斯模糊图像数据作USM锐化   
  DoUSMSharpen(Data, Src, (Amount shl 9) div 100, Threshold);  
  FreeImageData(Src);  
  if IsInvertScan0 then  
    _InvertScan0(Data);  
end;  
 
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 xmm2, 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       esi
end;
 
// --> st x
// <-- st e**x = 2**(x*log2(e))
function _Expon: Extended;
asm
    fldl2e              // y = x*log2e
    fmul
    fld     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;
  _fc255: Single = 255.0;
var
  R: Integer;
  v, QQ2: double;
asm
    mov     R, ecx
    mov     ecx, eax
    fld     Q
    fabs
    fcom    _fcd1
    fstsw   ax
    sahf
    jae     @@1
    fld     _fcd1
    fstp    st(1)               // if (Q < 0.1) Q = 0.1
    jmp     @@2
@@1:
    fcom    _fc250
    fstsw   ax
    sahf
    jbe     @@2
    fld     _fc250
    fstp    st(1)               // if (Q > 250) Q = 250
@@2:
    fst     Q
    fmul    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
@@testRadius:                   //   while (TRUE)
    mov     R, eax              //   {
    fldz                        //     sum = 0
@@testLoop:                     //     for (R = radius; R > 0; R ++)
    fild    R                   //     {
    fld     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          // weights = ((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
    fld     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 ++)
    fld     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;
 
// 高斯模糊。
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
//    PArgbConvertArgb(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 = 原像素
    movd      mm2, [edi]  // mm2 = 高斯模糊像素
    movq      mm0, mm1
    punpcklbw mm1, mm7
    punpcklbw mm2, mm7
    psubw     mm1, mm2
    psllw     mm1, 7
    pmulhw    mm1, mm5    // mm1 = (原像素 - 高斯模糊像素) * 数量
    pxor      mm2, mm2
    psubw     mm2, mm1    // mm2 = -mm1
    psubw     mm1, mm6    // mm1 -= 阈值
    psubw     mm2, mm6    // mm2 -= 阈值
    packuswb  mm1, mm7    // mm1 = 正离差(小于0的值饱和为0)
    packuswb  mm2, mm7    // mm2 = 负离差(小于0的值饱和为0)
    paddusb   mm0, mm1
    psubusb   mm0, mm2    // mm0 = 原像素 + 正离差 - 负离差
    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
    emms
end;
 
// USM锐化。参数:图像数据,半径,数量,阈值
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;
  // 备份图像数据
  Src := NewImageData(Data.Width, Data.Height);
  IsInvertScan0 := Data.Stride < 0;
  if IsInvertScan0 then
    _InvertScan0(Data);
  CopyData(Src, Data);
  // 图像数据作高斯模糊
  ImageGaussiabBlur(Data, Radius, 0);
  // 用备份图像数据和高斯模糊图像数据作USM锐化
  DoUSMSharpen(Data, Src, (Amount shl 9) div 100, Threshold);
  FreeImageData(Src);
  if IsInvertScan0 then
    _InvertScan0(Data);
end;
    下面是个简单的调用例子:
 
[delphi]  
procedure TForm1.Button2Click(Sender: TObject);  
var  
  bmp, bmp2: TBitmap;  
  data, data2: TImageData;  
begin  
  // 从文件装入图像数据到bmp,并绑定到data   
  bmp := TBitmap.Create;  
  bmp.LoadFromFile('d:\source.bmp');  
  data := GetBitmapData(bmp);  
  // USM锐化:半径3.0,数量150,阈值10   
  ImageUSMSharpen(data, 3, 150, 10);  
  // 画USM锐化后的图像   
  Canvas.Draw(0, 0, bmp);  
  // 从文件装入Photoshop USM锐化图像数据到bmp2,并绑定到data2   
  bmp2 := TBitmap.Create;  
  bmp2.LoadFromFile('d:\sourceUSM.bmp');  
  data2 := GetBitmapData(bmp2);  
  // 将ImageUSMSharpen过程锐化与Photoshop USM锐化图像数据作比较   
  ImageCompare(data, data2);  
  bmp2.Free;  
  bmp.Free;  
end;  
 
procedure TForm1.Button2Click(Sender: TObject);
var
  bmp, bmp2: TBitmap;
  data, data2: TImageData;
begin
  // 从文件装入图像数据到bmp,并绑定到data
  bmp := TBitmap.Create;
  bmp.LoadFromFile('d:\source.bmp');
  data := GetBitmapData(bmp);
  // USM锐化:半径3.0,数量150,阈值10
  ImageUSMSharpen(data, 3, 150, 10);
  // 画USM锐化后的图像
  Canvas.Draw(0, 0, bmp);
  // 从文件装入Photoshop USM锐化图像数据到bmp2,并绑定到data2
  bmp2 := TBitmap.Create;
  bmp2.LoadFromFile('d:\sourceUSM.bmp');
  data2 := GetBitmapData(bmp2);
  // 将ImageUSMSharpen过程锐化与Photoshop USM锐化图像数据作比较
  ImageCompare(data, data2);
  bmp2.Free;
  bmp.Free;
end;
    下面是原始图像与本文USM锐化例子运行界面截图:
 
\
 
\
 
    从运行界面上看,本文USM锐化与Photoshop的USM锐化有一定的误差,这个误差并不大,完全可以接受。但需要说明的是,这里的误差是高斯模糊和USM锐化2个处理结果共同产生的误差,而《Delphi图像处理 -- 高斯模糊》中高斯模糊实现与Photoshop高斯模糊只是近似,因此,有必要排除高斯模糊影响,来具体确定本文USM锐化算法与Photoshop的真实误差,下面的例子是用Photoshop处理的高斯模糊图片与原图作USM锐化,这时的误差将完全是本文USM锐化代码产生的:
 
[delphi]  
procedure TForm1.Button1Click(Sender: TObject);  
var  
  bmp, bmp2: TBitmap;  
  data, data2: TImageData;  
begin  
  // 从文件装入Photoshop高斯模糊(半径3.0)图像数据到bmp,并绑定到data   
  bmp := TBitmap.Create;  
  bmp.LoadFromFile('d:\sourceBlur.bmp');  
  data := GetBitmapData(bmp);  
  // 从文件装入图像数据到bmp2,并绑定到data2   
  bmp2 := TBitmap.Create;  
  bmp2.LoadFromFile('d:\source.bmp');  
  data2 := GetBitmapData(bmp2);  
  // 用Photoshop高斯模糊图像数据与原数据作USM锐化:数量150,阈值10   
  DoUSMSharpen(data, data2, (150 shl 9) div 100, 10);  
  // 画USM锐化后的图像   
  Canvas.Draw(0, 0, bmp);  
  // 从文件装入Photoshop USM锐化图像数据到bmp2,并绑定到data2   
  bmp2.LoadFromFile('d:\sourceUSM.bmp');  
  data2 := GetBitmapData(bmp2);  
  // 将DoUSMSharpen过程锐化与Photoshop USM锐化图像数据作比较   
  ImageCompare(data, data2);  
  bmp2.Free;  
  bmp.Free;  
end;  
 
procedure TForm1.Button1Click(Sender: TObject);
var
  bmp, bmp2: TBitmap;
  data, data2: TImageData;
begin
  // 从文件装入Photoshop高斯模糊(半径3.0)图像数据到bmp,并绑定到data
  bmp := TBitmap.Create;
  bmp.LoadFromFile('d:\sourceBlur.bmp');
  data := GetBitmapData(bmp);
  // 从文件装入图像数据到bmp2,并绑定到data2
  bmp2 := TBitmap.Create;
  bmp2.LoadFromFile('d:\source.bmp');
  data2 := GetBitmapData(bmp2);
  // 用Photoshop高斯模糊图像数据与原数据作USM锐化:数量150,阈值10
  DoUSMSharpen(data, data2, (150 shl 9) div 100, 10);
  // 画USM锐化后的图像
  Canvas.Draw(0, 0, bmp);
  // 从文件装入Photoshop USM锐化图像数据到bmp2,并绑定到data2
  bmp2.LoadFromFile('d:\sourceUSM.bmp');
  data2 := GetBitmapData(bmp2);
  // 将DoUSMSharpen过程锐化与Photoshop USM锐化图像数据作比较
  ImageCompare(data, data2);
  bmp2.Free;
  bmp.Free;
end;
    运行界面:
 
\
 
    运行结果,完全没有误差!证明本文USM锐化算法和步骤是正确的。
 
    前面的2个例子中,都调用了图像数据比较过程ImageCompare,也将它列在下面:
 
[delphi]  
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  
      begin  
    Inc(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  
      begin  
    Inc(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  
      begin  
    Inc(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('像素总数:%d' + #13 + #10 +  
              '红误差数:%d,误差率:%d%%,最大误差:%d' + #13 + #10 +  
              '绿误差数:%d,误差率:%d%%,最大误差:%d' + #13 + #10 +  
              '蓝误差数:%d,误差率:%d%%,最大误差:%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;  
 
Tags:

文章评论

最 近 更 新
热 点 排 行
Js与CSS工具
代码转换工具

<