阅读提示:
《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。
《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。
尽可能保持二者内容一致,可相互对照。
本文代码必须包括文章《Delphi图像处理 -- 数据类型及公用过程》中的ImageData.pas单元。
图像的中值滤波,就是在以某一像素为中心的n阶像素矩阵中,找出R、G、B各分量的中间值来分别替代该像素的RGB值,从而达到对图像噪声滤波的目的。这里的中间值并非像素矩阵R、G、B各分量的的算术平均值,而是像素矩阵R、G、B各分量排序后的中位数值。
下面是Delphi图像中值滤波的实现代码:
procedure MedianValues(var Dest: TImageData; const Source: TImageData;
buf: Pointer; SortSize, Size, MatrixOffset: Integer);
var
width, height: Integer;
dstOffset, srcOffset: Integer;
median: Pointer;
procedure MedianSort;
asm
mov edx, edi // i = count - 1
mov al, [esi] // al = r(gb)
@@Loop: // while (i >= 0 && buf[i].r(rb) > al) i --;
sub edx, 4
js @@1
cmp al, [ebx+edx]
jb @@Loop
@@1:
add edx, 4 // i ++
mov ecx, edi //
@@moveLoop:
cmp ecx, edx // memmove(buf[i], buf[i+1], count - i)
je @@2
mov ah, [ebx+ecx-4]
mov [ebx+ecx], ah
sub ecx, 4
jmp @@moveLoop
@@2:
mov [ebx+edx], al // buf[i] = al
inc ebx
inc esi
end;
asm
push esi
push edi
push ebx
push ecx
call _SetCopyRegs
mov width, ecx
mov height, edx
mov dstOffset, ebx
mov srcOffset, eax
pop ebx
mov eax, ebx
add eax, SortSize
sub eax, 4
mov median, eax
@@yLoop:
push width
@@xLoop:
push esi
push edi
xor edi, edi
mov ecx, Size
@@iLoop:
push ecx
mov ecx, Size
@@jLoop:
push ecx
call MedianSort
call MedianSort
call MedianSort
inc esi
sub ebx, 3
cmp edi, SortSize
je @@1
add edi, 4
@@1:
pop ecx
loop @@jLoop
add esi, MatrixOffset
pop ecx
loop @@iLoop
pop edi
pop esi
mov eax, median
mov eax, [eax]
mov cl, [edi].TARGBQuad.Alpha
mov [edi], eax
mov [edi].TARGBQuad.Alpha, cl
add esi, 4
add edi, 4
dec width
jnz @@xLoop
add esi, srcOffset
add edi, dstOffset
pop width
dec height
jnz @@yLoop
pop ebx
pop edi
pop esi
end;
procedure MedianValues3(var Dest: TImageData; const Source: TImageData; buf: Pointer; MatrixOffset: Integer);
var
width, height: Integer;
dstOffset, srcOffset: Integer;
median: Pointer;
procedure AssortValue;
asm
mov ah, [esi]
mov dl, [esi+4]
mov al, [esi+8]
cmp ah, al
jae @@1
xchg ah, al
@@1:
cmp ah, dl
jae @@2
xchg ah, dl
@@2:
cmp al, dl
jbe @@3
xchg al, dl
@@3:
mov [ebx], ah // ah = large
mov [ebx+4], dl // dl = center
mov [ebx+8], al // al = small
inc esi
inc ebx
end;
procedure GetValue;
asm
mov ah, [ebx] // large group: ebx ebx+12 ebx+24
mov dl, [ebx+4] // center group: ebx+4 ebx+16 ebx+28
mov al, [ebx+8] // small group: ebx+8 ebx+20 ebx+32
mov dh, [ebx+16]
cmp ah, [ebx+12] // ah = min of large group
jbe @@1
mov ah, [ebx+12]
@@1:
cmp ah, [ebx+24]
jbe @@2
mov ah, [ebx+24]
@@2:
cmp dh, dl
jae @@3
xchg dh, dl
@@3:
cmp dh, [ebx+28]
jae @@4
xchg dh, [ebx+28]
@@4:
cmp dl, [ebx+28] // dl = median of center group
jae @@5
mov dl, [ebx+28]
@@5:
cmp al, [ebx+20] // al = max of small group
jae @@6
mov al, [ebx+20]
@@6:
cmp al, [ebx+32]
jae @@7
mov al, [ebx+32]
@@7:
cmp ah, al
jae @@8
xchg al, ah
@@8:
cmp ah, dl
jae @@9
xchg ah, dl
@@9:
cmp al, dl // al = median of [ah, dl, al]
jae @@10
mov al, dl
@@10:
mov [edi], al
inc edi
inc ebx
end;
asm
push esi
push edi
push ebx
push ecx
call _SetCopyRegs
mov width, ecx
mov height, edx
mov dstOffset, ebx
mov srcOffset, eax
add MatrixOffset, 9
pop ebx
@@yLoop:
push width
@@xLoop:
push esi
push ebx
mov ecx, 3
@@mLoop:
call AssortValue
call AssortValue
call AssortValue
add ebx, 9
add esi, MatrixOffset
loop @@mLoop
pop ebx
pop esi
call GetValue
call GetValue
call GetValue
add esi, 4
sub ebx, 3
inc edi
dec width
jnz @@xLoop
add esi, srcOffset
add edi, dstOffset
pop width
dec height
jnz @@yLoop
pop ebx
pop edi
pop esi
end;
procedure ImageMedianValues(var Data: TImageData; Radius: Integer);
var
exp: TImageData;
Buf: array of Byte;
Size, SortSize: Integer;
MatrixOffset: Integer;
begin
Size := (Radius shl 1) + 1;
exp := _GetExpandData(Data, Radius);
MatrixOffset := exp.Stride - (Size shl 2);
try
if Radius = 1 then
begin
SetLength(Buf, 9 * Sizeof(TARGBQuad));
MedianValues3(Data, exp, Buf, MatrixOffset);
end
else
begin
SortSize := ((Size * Size + 1) shr 1) * Sizeof(TARGBQuad);
SetLength(Buf, SortSize + Sizeof(TARGBQuad));
MedianValues(Data, exp, Buf, SortSize, Size, MatrixOffset);
end;
finally
FreeImageData(exp);
end;
end;
由于中值滤波要对每个像素都采用n阶矩阵排序的方法找出其R、G、B分量的中间值,因此该操作是非常耗时的。最大的耗时主要是排序过程,尽管本文中值滤波过程用了BASM代码,但这个滤波过程还是较慢,显然排序算法是提高操作速度的关键,我试验了多种排序算法,都不理想,没办法,只好将最常用的3阶中值滤波排序进行了改进,所以,本文中值滤波过程处理图像的3阶中值滤波速度相对还是较快的;对于半径大于1(即3*3以上的)的中值滤波排序,改为插入排序,只比较小于中值的数据,大于或等于中值的数据直接忽略,因为我们需要只是的中间值,对于大于中间值的数据排序无疑是浪费时间!如此节省了不少时间,处理时间比以前平均节省了20%。不过还是比较耗时。
《Delphi图像处理》系列使用GDI+单元下载地址和说明见文章《GDI+ for VCL基础 -- GDI+ 与 VCL》。
因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com
这里可访问《Delphi图像处理 -- 文章索引》。