阅读提示:
《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》。
因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:[email protected]
这里可访问《Delphi图像处理 -- 文章索引》。