閱讀提示:
《Delphi影像處理》系列以效率為側重點,一般代碼為PASCAL,核心代碼採用BASM。
《C++影像處理》系列以代碼清晰,可讀性為主,全部使用C++代碼。
儘可能保持二者內容一致,可相互對照。
本文代碼必須包括文章《Delphi影像處理 -- 資料類型及公用過程》中的ImageData.pas單元和《Delphi影像處理 -- 平面幾何變換類》TransformMatrix.pas單元。
實現映像浮雕效果的一般原理是,將映像上每個像素點與其對角線的像素點形成差值,使類比色彩值淡化,不同顏色值突出,從而產生縱深感,達到浮雕的效果,具體的做法是用處於對角線的2個像素值相減,再加上一個背景常數,一般為128而成。這種演算法的特點是簡單快捷,缺點是不能調節映像浮雕效果的角度和深度。
用Photoshop實現的映像浮雕效果,可以任意調節浮雕角度和深度(2個像素點的距離),還可以調整浮雕像素差值的數量。其基本演算法原理和一般浮雕效果相同,但是具體做法不一樣:對每個要處理的像素點,首先按照浮雕角度和深度計算處2個相應點的位置,然後計算這2個位置的顏色值,並使之形成差值,再乘上浮雕差值數量百分比,最後加上128的背景色。注意,這裡計算的2個相應點是邏輯點,而不是實際的像素點,比如實現一個45度角,深度為3的映像浮雕效果,對每個像素點P(x, y),其對應的2個邏輯點的位置分別是P0(x - 3 * 0.7071 / 2, y - 3 * 0.7071 / 2)和P1(x + 3 * 0.7071 / 2, y + 3 * 0.7071 / 2),顯然,對於這樣的2個邏輯點,是不能直接從映像中找到其對應的像素點的,如果簡單地對其四捨五入處理,將會造成大量的,由不同角度和深度而形成的相同的浮雕效果,這可不是我們想要的結果,而且使浮雕角度和深度參數失去了它原本的意義。為此,必須對原始映像按浮雕角度和深度進行縮放後,再對每個像素點進行浮雕效果處理,完畢再縮放回原圖的大小,從而完成整個浮雕效果過程。下面是我經過反覆實驗後,寫的Photoshop浮雕效果實現過程代碼:
procedure GetSrcColor;asm push esi // esi = src.Scan0 mov eax, ecx sar eax, 12 imul eax, ebx add esi, eax // esi += (y / 4096 * src.Stride) mov eax, edx sar eax, 12 shl eax, 2 add esi, eax // esi += (x / 4096 * 4) call _GetBilinearColor movd eax, xmm0 movd mm0, eax punpcklbw mm0, mm7 // return mm0 = ARGB (word * 4) pop esiend;procedure ImageGraySculpture(var Data: TImageData; Angle: Single; Size: LongWord; Num: LongWord = 100);var x, y, Radius: Integer; xDelta, yDelta: Integer; width, height: Integer; dstOffset: Integer; src: TImageData;begin Radius := (Size + 1) shr 1; // 映像邊框擴充半徑 Angle := PI * Angle / 180; Size := Size shl 12; // Size = Size * 256 Num := (Num shl 9) div 100; // Num *= 5.12 轉換為2的冪 xDelta := Round(Cos(Angle) * Size); yDelta := Round(Sin(Angle) * Size); x := (Radius shl 12) - (xDelta div 2); y := (Radius shl 12) - (yDelta div 2); width := x + (Data.Width shl 12); height := y + (Data.Height shl 12); if Data.AlphaFlag then ArgbConvertPArgb(Data); src := _GetExpandData(Data, Radius); asm mov eax, Data lea edx, src call _SetCopyRegs mov dstOffset, ebx movq mm5, qword ptr ArgbTab[128*8]// mm5 = 00 128 00 128 00 128 00 128 movd mm6, Num // mm6 = 00 Num 00 Num 00 Num 00 Num punpcklwd mm6, mm6 punpcklwd mm6, mm6 pxor mm7, mm7 // mm7 = 00 00 00 00 00 00 00 00 pxor xmm7, xmm7 mov esi, src.Scan0 mov ebx, src.Stride mov ecx, y // for (; y < Height; y += 4096)@@yLoop: // {mov edx, x // for (; x < Width; x += 4096)@@xLoop: // { add ecx, yDelta // y1 = y + yDelta add edx, xDelta // x1 = x + xDelta call GetSrcColor // movq mm1, mm0 // mm1 = 00 A1 00 R1 00 G1 00 B1 sub ecx, yDelta // y0 = y - yDelta sub edx, xDelta // x0 = x - xDelta call GetSrcColor // mm0 = 00 A0 00 R0 00 G0 00 B0 psubw mm0, mm1 // mm0 = A0-A1 R0-R1 G0-G1 B0-B1 psllw mm0, 7 // mm0 = mm0 * 128 * Num / 65536 pmulhw mm0, mm6 paddw mm0, mm5 // mm0 = A+128 R+128 G+128 B+128 packuswb mm0, mm7 // mm0 = 00 00 00 00 A R G B mov al, [edi].TARGBQuad.Alpha movd [edi], mm0 // *edi = mm0 mov [edi].TARGBQuad.Alpha, al add edi, 4 // edi += 4 add edx, 1000h cmp edx, width jl @@xLoop // } add ecx, 1000h add edi, dstOffset cmp ecx, height jl @@yLoop // } emms end; FreeImageData(src); if Data.AlphaFlag then PArgbConvertArgb(Data);end;
下面是個示範例子代碼:
procedure TForm1.Button3Click(Sender: TObject);var bmp: TGpBitmap; g: TGpGraphics; data: TImageData;begin bmp := TGpBitmap.Create('..\media\20041001.jpg'); g := TGpGraphics.Create(Canvas.Handle); g.DrawImage(bmp, 0, 0); data := LockGpBitmap(bmp); ImageGraySculpture(data, 45, 3, 100); UnlockGpBitmap(bmp, data); g.DrawImage(bmp, 0, data.Height); g.Free; bmp.Free;end;
運行:
和Photoshop浮雕效果對比,基本一致。
補記:在文章評論中,有人說灰色浮雕不帶彩,其實是不正確的,我測試過網上和書上的很多代碼,也測試過Photoshop,都是有可能帶彩的,在浮雕深度很小而且原始映像色彩較平淡時也有可能不帶彩,可以把我上面的原圖裁下來,用Photoshop一試就清楚了。當然,如果你要達到完全不帶彩也很簡單,在浮雕處理前做一個映像灰階化即可。如果灰階浮雕完全不帶彩,程式碼可得到簡化,速度至少可再提高60%以上。
《Delphi影像處理》系列使用GDI+單元和說明見文章《GDI+ for VCL基礎 -- GDI+ 與 VCL》。
因水平有限,錯誤在所難免,歡迎指正和指導。郵箱地址:maozefa@hotmail.com
這裡可訪問《Delphi影像處理 -- 文章索引》。