閱讀提示:
《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 esiend;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 esiend;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影像處理 -- 文章索引》。