閱讀提示:
《Delphi影像處理》系列以效率為側重點,一般代碼為PASCAL,核心代碼採用BASM。
《C++影像處理》系列以代碼清晰,可讀性為主,全部使用C++代碼。
儘可能保持二者內容一致,可相互對照。
本文代碼必須包括文章《Delphi影像處理 -- 資料類型及公用過程》中的ImageData.pas單元。
映像的表面模糊處理是Photoshop CS2以後才有的新功能,其作用是在保留映像邊緣的情況下,對映像的表面進行模糊處理。在對人物皮膚處理上,比高斯模糊更有效。因為高斯模糊在使人物皮膚光潔的同時,也將一些邊緣特徵如臉部的眉毛、嘴唇等給模糊了,不得不用蒙版小心的抹去這些地方的模糊部分。
在處理手法上,表面模糊也與其它卷積處理手段不同,如高斯模糊等在處理映像時都是採用統一的卷積矩陣進行,而表面模糊卻是每一個像素點都有自己的卷積矩陣,而且還是3(4)套,用以對應於像素的R、G、B(A、R、G、B)分量。所以表面模糊在編程處理時,比其它卷積操作更複雜、更耗時,因為它要對每一個像素計算自己的卷積矩陣。表面模糊編程的痛點也在計算卷積矩陣上,其它與一般映像卷積處理一樣。
表面模糊處理有2個參數,即模糊半徑和模糊閾值,前者確定模糊的範圍,後者確定模糊的程度。模糊範圍就是卷積矩陣大小,如模糊半徑為1,則模糊矩陣直徑為1*2+1等於3,矩陣元素個數為3*3等於9,矩陣的中間元素即是當前像素點。
矩陣元素值的計算公式為:
1) mij = 1 - (|pij - p0|) / 2.5T
變形後可得:
2) mij = (2.5T - ([pij - p0])) / 2.5T
其中,mij為矩陣的各元素值,pij為矩陣元素對應的像素分量值,p0為矩陣中心元素對應的像素分量值,T為閾值,|pij - p0|為矩陣元素對應像素分量值與中心元素對應像素分量值的絕對差值。如果mij < 0,則mij = 0。
對ARGB格式映像資料來說,因有4個分量,故需要4套卷積矩陣。
矩陣元素確定後,就可按照一般映像卷積操作進行處理了,即分別累計矩陣元素值和與之對應的像素分量值乘積,用累計像素分量值除以累計元素值,即可得到當前像素分量模糊處理後的值。
下面是表面模糊處理代碼:
// ARGB映像資料表面模糊處理.// 參數: 映像資料, 模糊半徑, 閾值procedure SurfaceBlur(var Data: TImageData; Radius, Threshold: Integer);const _fc2_5: Single = 2.5;var width, height, size: Integer; srcOffset, dstOffset: Integer; src: TImageData;begin if not (Radius in [1..100]) or not (Threshold in [2..255]) then Exit; if Data.AlphaFlag then // 如果映像資料含Alpha資訊, 轉換為PARGB格式 ArgbConvertPArgb(Data); src := _GetExpandData(Data, Radius);// 擷取擴充半徑後的備份映像資料 asm push esi push edi push ebx mov eax, Data lea edx, src call _SetCopyRegs mov width, ecx mov height, edx mov srcOffset, eax mov dstOffset, ebx mov eax, src.Stride mov ebx, Radius shl ebx, 1 inc ebx mov size, ebx shl ebx, 2 neg ebx add ebx, eax add eax, 4 mul Radius pxor xmm7, xmm7 cvtsi2ss xmm6, Threshold // xmm6 = 4 * (Threshold * 2.5) movss xmm0, _fc2_5 mulss xmm6, xmm0 pshufd xmm6, xmm6, 0@@yLoop: push width@@xLoop: push esi mov edx, Size pxor xmm0, xmm0 // Total Pixels pxor xmm5, xmm5 // Nuclear movd xmm4, [esi+eax] // xmm4 = p0 = 4 * word(a,r,g,b) punpcklbw xmm4, xmm7@@iLoop: mov ecx, Size@@jLoop: movd xmm3, [esi] punpcklbw xmm3, xmm7 movaps xmm1, xmm3 movaps xmm2, xmm4 // 計算模糊矩陣元素值 psubw xmm1, xmm4 // xmm1 = xij - x0 psubw xmm2, xmm3 // xmm2 = x0 - xij packuswb xmm1, xmm7 // 位元組飽和去掉xmm1中各分量差值中的負值 packuswb xmm2, xmm7 // 位元組飽和去掉xmm2中各分量差值中的負值 por xmm2, xmm1 // xmm2 = |xij - x0| punpcklbw xmm2, xmm7 punpcklwd xmm2, xmm7 punpcklwd xmm3, xmm7 cvtdq2ps xmm3, xmm3 cvtdq2ps xmm2, xmm2 movaps xmm1, xmm6 subps xmm1, xmm2 divps xmm1, xmm6 // xmm1 = (xmm6 - xmm2) / xmm6 maxps xmm1, xmm7 // 去掉xmm1中的負值 mulps xmm3, xmm1 // xij *= xmm1 // 累計模糊矩陣元素值和與對應像素值的乘積 addps xmm5, xmm1 // Nuclear += xmm1 addps xmm0, xmm3 // Total Pixels += xij add esi, 4 loop @@jLoop add esi, ebx dec edx jnz @@iLoop divps xmm0, xmm5 // p0 = Total Pixels /= Nuclear cvtps2dq xmm0, xmm0 packssdw xmm0, xmm7 packuswb xmm0, xmm7 movd [edi], xmm0 pop esi add esi, 4 add edi, 4 dec width jnz @@xLoop pop width add esi, srcOffset add edi, dstOffset dec height jnz @@yLoop pop ebx pop edi pop esi end; FreeImageData(src); if Data.AlphaFlag then // 如果映像資料含Alpha資訊, 還原為ARGB格式 PArgbConvertArgb(Data);end;
例子運行效果:
《Delphi影像處理》系列使用GDI+單元和說明見文章《GDI+ for VCL基礎 -- GDI+ 與 VCL》。
因水平有限,錯誤在所難免,歡迎指正和指導。郵箱地址:maozefa@hotmail.com
這裡可訪問《Delphi影像處理 -- 文章索引》。