USM銳化是用來銳化圖像邊緣的,它通過調整圖像邊緣細節的對比度,並在邊緣的兩側生成一條亮線一條暗線,使畫面整體更加清晰。
USM銳化用公式描述很麻煩,這裡干脆實現步驟列於下面:
1、備份圖像原數據;
2、按給定半徑對圖像進行高斯模糊;
3、用原像素與高斯模糊後的像素相減,形成一個差值;
4、將差值乘以數量;
5、將差值分為正負2部分,取絕對值;
6、正負差值分別減去給定的阈值;
7、原像素加上正差值減去負差值,銳化完畢。
下面是USM銳化代碼,包括高斯模糊代碼(關於高斯模糊見文章《Delphi圖像處理 -- 高斯模糊》,下面的高斯模糊代碼也是從該文拷貝來的):
[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;