Delphi图像处理 -- Photoshop浮雕效果

实现图像浮雕效果的一般原理是,将图像上每个像素点与其对角线的像素点形成差值,使相似颜色值淡化,不同颜色值突出,从而产生纵深感,达到浮雕的效果,具体的做法是用处于对角线的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浮雕效果实现过程代码:

过程定义: // 灰色浮雕。参数: // Dest输出图,Source原图,Data自身操作图像 // Angle 角度, Size 长度, Num 像素数量百分比 // Callback回调函数,返回True终止操作,CallbackData回调函数参数地址 procedure ImageGraySculpture(var Data: TImageData; Angle: Single; Size: LongWord; Num: LongWord = 100); overload; {$IF RTLVersion >= 17.00}inline;{$IFEND} procedure ImageGraySculpture(var Dest: TImageData; const Source: TImageData; Angle: Single; Size: LongWord; Num: LongWord = 100); overload; {$IF RTLVersion >= 17.00}inline;{$IFEND} // 无效参数或者被回调函数终止操作返回False。 function ImageGraySculpture(var Dest: TImageData; const Source: TImageData; Angle: Single; Size: LongWord; Num: LongWord; Callback: TImageAbort; CallbackData: Pointer): Boolean; overload; 代码实现: procedure GetSrcColor; asm push edx // edx = x push ecx // ecx = y mov esi, ecx // esi = y / 256 * Data.Stride + mov eax, edx // x / 256 * 4 + Data.Scan0 sar esi, 8 sar eax, 8 shl eax, 2 imul esi, [ebx].TImageData.Stride add esi, eax add esi, [ebx].TImageData.Scan0 call GetBilinearColor pop ecx // return eax = mm0 = ARGB pop edx end; procedure GraySculpture(x, y, xDelta, yDelta, Num: Integer); pascal; var width, height: Integer; dstOffset: Integer; colorBuf: TARGBQuadW; data: TImageData; asm mov dstOffset, ebx lea ebx, data mov [ebx].TImageData.Scan0, esi shl ecx, 2 add eax, ecx // Stride = srcOffset + width * 4 mov [ebx].TImageData.Stride, eax shl ecx, 6 add ecx, x mov width, ecx // width = width * 256 + x shl edx, 8 add edx, y mov height, edx // height = height * 256 + y movd mm6, Num punpcklwd mm6, mm6 punpcklwd mm6, mm6 // mm6 = 00 Num 00 Num 00 Num 00 Num pxor mm7, mm7 // mm7 = 00 00 00 00 00 00 00 00 mov ecx, y // for (; y < Height; y += 256) @@yLoop: // { mov edx, x // for (; x < Width; x += 256 @@xLoop: // { add ecx, yDelta // y1 = y + yDelta add edx, xDelta // x1 = x + xDelta call GetSrcColor // mm0 = 00 00 00 00 A1 R1 G1 B1 punpcklbw mm0, mm7 // mm0 = 00 A1 00 R1 00 G1 00 B1 movq ColorBuf, mm0 // ColorBuf = mm0 sub ecx, yDelta // y0 = y - yDelta sub edx, xDelta // x0 = x - xDelta call GetSrcColor // mm0 = 00 00 00 00 A0 R0 G0 B0 punpcklbw mm0, mm7 // mm0 = 00 A0 00 R0 00 G0 00 B0 psubsw mm0, ColorBuf // mm0 = A0-A1 R0-R1 G0-G1 B0-B1 psllw mm0, 7 // mm0 = mm0 * 128 * Num / 65536 pmulhw mm0, mm6 paddsw mm0, qword ptr ArgbTable[128*8]// 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, 256 cmp edx, Width jl @@xLoop // } add ecx, 256 add edi, dstOffset cmp ecx, Height jl @@yLoop // } emms end; function ImageGraySculpture(var Dest: TImageData; const Source: TImageData; Angle: Single; Size: LongWord; Num: LongWord; Callback: TImageAbort; CallbackData: Pointer): Boolean; var x, y, Radius: Integer; xDelta, yDelta: Integer; dst, src: TImageData; begin Result := False; if ImageEmpty(Dest) or ImageEmpty(Source) or (Size = 0) then Exit; Radius := (Size + 1) shr 1; // 图像边框扩展半径 Angle := PI * Angle / 180; Size := Size shl 8; // 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 8) - (xDelta div 2); y := (Radius shl 8) - (yDelta div 2); if @Dest <> @Source then CopyAlpha(Dest, Source); GetExpandData(Dest, Source, Radius, dst, src); try if Assigned(Callback) then Result := ExecuteAbort(dst, src, @GraySculpture, [x, y, xDelta, yDelta, Num], Callback, CallbackData) else Result := ExecuteProc(dst, src, @GraySculpture, [x, y, xDelta, yDelta, Num]); finally FreeImageData(src); end; end; procedure ImageGraySculpture(var Dest: TImageData; const Source: TImageData; Angle: Single; Size: LongWord; Num: LongWord); begin ImageGraySculpture(Dest, Source, Angle, Size, Num, nil, nil); end; procedure ImageGraySculpture(var Data: TImageData; Angle: Single; Size: LongWord; Num: LongWord); begin ImageGraySculpture(Data, Data, Angle, Size, Num, nil, nil); end;

下面是个演示例子代码:

type TForm1 = class(TForm) PaintBox1: TPaintBox; Label1: TLabel; Edit1: TEdit; Label2: TLabel; Edit2: TEdit; Label3: TLabel; Edit3: TEdit; Button1: TButton; Button2: TButton; procedure Edit1KeyPress(Sender: TObject; var Key: Char); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure PaintBox1Paint(Sender: TObject); private { Private declarations } FData: TImageData; FSource: TImageData; public { Public declarations } end; var Form1: TForm1; implementation uses Gdiplus, GpBitmapUtils; {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var Angle: Single; Size: LongWord; Num: LongWord; begin CopyImageDataTo(FData, FSource); Angle := StrToFloat(Edit1.Text); Size := StrToInt(Edit2.Text); Num := StrToInt(Edit3.Text); ImageGraySculpture(FData, Angle, Size, Num); PaintBox1.Invalidate; end; procedure TForm1.Button2Click(Sender: TObject); begin Close; end; procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); begin if (Key >= #32) and not (Key in ['0'..'9']) then Key := #0; end; procedure TForm1.FormCreate(Sender: TObject); begin FSource := GetImageData(TGpBitmap.Create('..\..\Media\20041001.jpg'), True); FData := NewImageData(FSource.Width, FSource.Height, 0); DoubleBuffered := True; Button1.Click; end; procedure TForm1.FormDestroy(Sender: TObject); begin FreeImageData(FData); FreeImageData(FSource); end; procedure TForm1.PaintBox1Paint(Sender: TObject); begin DrawImage(PaintBox1.Canvas, 0, 0, FSource); DrawImage(PaintBox1.Canvas, 0, FSource.Height, FData); end;

例子中的DrawImage过程见《Delphi图像处理 -- 图像显示》。

运行效果图:

和Photoshop浮雕效果对比,基本一致,说明我对其算法实现了完全“破解”,虽然这个算法不是很难,但也费了我不少时间。

文章中使用GDI+版本下载地址和说明见《GDI+ for VCL基础 -- GDI+ 与 VCL》。

文章中所用数据类型及一些过程见《Delphi图像处理 -- 数据类型及内部过程》,《Delphi图像处理 -- 图像像素结构与图像数据转换》和《Delphi图像处理 -- 图像缩放》。

尽管我十分努力,但水平有限,错误在所难免,欢迎指正和指导。邮箱地址:

maozefa@hotmail.com

后记:在文章评论中,有人说灰色浮雕不带彩,其实是不正确的,我测试过网上和书上的很多代码,也测试过Photoshop,都是有可能带彩的,在浮雕深度很小而且原始图像色彩较平淡时也有可能不带彩,可以把我上面的原图裁下来,用Photoshop一试就清楚了。当然,如果你要达到完全不带彩也很简单,在浮雕处理前做一个图像灰度化即可。如果灰度浮雕完全不带彩,程序代码可得到简化,速度至少可再提高60%以上。

本文代码于2010.5.20重新修订过。增加了拷贝形式的调整过程和响应回调函数的调整过程。代码中的ExecuteAbort过程和ExecuteProc过程见《Delphi图像处理 -- 图像像素结构与图像数据转换》。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值