Delphi图像处理 -- 图像合成

图像合成是指将一张图像的全部或者局部与另一张图像合并,形成一张新的图像。图像合成的基本形式有2种:

一是按图像源像素本身的不透明度信息合成到目标图片上,这是最简单的形式,像素的合并公式为:

nRGB = (sAlpha * sRGB + (255 - sAlpha) * dRGB) / 255

公式中,nRGB为新的R(G,B)分量,sRGB为源的R(G,B)分量,dRGB为目标的R(G,B)分量,sAlpha为源的不透明度。

二是用指定的不透明度将图像源合成到目标图片上,像素的合并公式为:

nRGB = (Alpha * sAlpha * sRGB + (255 - Alpha * sAlpha) * dRGB) / 255

如果图像源的像素本身不含不透明度信息,则sAlpha = 255:

nRGB = (Alpha * 255 * sRGB + (255 - Alpha * 255) * dRGB) / 255

公式中,Alpha为指定的不透明度,取值范围为0 -- 1,其它同上。

本文将提供上述2种形式的图像合成过程,在实现代码中,为了加快合成速度,采用了像素合并简化公式,如前述的第一种形式公式将简化为:

nRGB = (sAlpha * (sRGB - dRGB) + dRGB * 256) / 256

简化后的公式与原公式有一些微小的误差,不过与人眼的视觉误差相比,可忽略不计。

在实际图像合成过程中,往往会要求将图像源的某个景物合成到目标图像上,这就要借助设置像素关键颜色,甚至羽化等手段将景物从图像源中抠出来,形成上面的2种基本形式后,再进行图像合并。这些已经不是本文讨论的范围。

下面是图像合成的代码:

过程定义: // 合成图像。Alpha合成源图像的不透明度(0 - 1) procedure ImageSysthesis(var Dest: TImageData; const Source: TImageData; x, y: Integer; Alpha: Single = 1.0); overload; procedure ImageSysthesis(var Dest: TImageData; const Source: TGraphic; x, y: Integer; Alpha: Single = 1.0); overload; procedure ImageSysthesis(var Dest: TImageData; Source: TGpBitmap; x, y: Integer; Alpha: Single = 1.0); overload; 实现代码: procedure DoSysthesis(var Dest: TImageData; const Source: TImageData; Alpha: Integer); var height, srcOffset: Integer; asm push esi push edi push ebx push ecx call SetCopyRegs32 mov srcOffset, eax pxor mm7, mm7 pop eax cmp eax, 256 jae @@yLoopB mov height, edx mov edx, alpha @@yLoopA: push ecx @@xLoopA: movzx eax, byte ptr [esi+3] imul eax, edx shr eax, 8 movd mm0, [esi] movd mm1, [edi] punpcklbw mm0, mm7 punpcklbw mm1, mm7 psubw mm0, mm1 pmullw mm0, qword ptr ArgbTable[eax*8] psllw mm1, 8 paddw mm0, mm1 psrlw mm0, 8 packuswb mm0, mm0 movd [edi], mm0 add esi, 4 add edi, 4 loop @@xLoopA add esi, srcOffset add edi, ebx pop ecx dec height jnz @@yLoopA jmp @@subReturn @@yLoopB: push ecx @@xLoopB: movzx eax, byte ptr [esi+3] movd mm0, [esi] movd mm1, [edi] punpcklbw mm0, mm7 punpcklbw mm1, mm7 psubw mm0, mm1 pmullw mm0, qword ptr ArgbTable[eax*8] psllw mm1, 8 paddw mm0, mm1 psrlw mm0, 8 packuswb mm0, mm0 movd [edi], mm0 add esi, 4 add edi, 4 loop @@xLoopB add esi, srcOffset add edi, ebx pop ecx dec edx jnz @@yLoopB @@subReturn: pop ebx pop edi pop esi emms end; procedure ImageSysthesis(var Dest: TImageData; const Source: TImageData; x, y: Integer; Alpha: Single); var dst, src: TImageData; begin if ImageEmpty(Source) then Exit; dst := GetSubImageData(Dest, x, y, Source.Width, Source.Height); if dst.Scan0 = nil then Exit; if x < 0 then x := -x else x := 0; if y < 0 then y := -y else y := 0; src := GetSubData(Source, x, y, dst.Width, dst.Height); DoSysthesis(dst, src, Round(Alpha * 256)); end; procedure ImageSysthesis(var Dest: TImageData; const Source: TGraphic; x, y: Integer; Alpha: Single); var src: TImageData; begin src := GetImageData(Source); ImageSysthesis(Dest, src, x, y, Alpha); FreeImageData(src); end; procedure ImageSysthesis(var Dest: TImageData; Source: TGpBitmap; x, y: Integer; Alpha: Single); var src: TImageData; begin src := GetImageData(Source); ImageSysthesis(Dest, src, x, y, Alpha); FreeImageData(src); end;

上面的代码中定义了多个图像合成过程,以满足不同情况、不同对象的图像合并,但图像合成的核心代码还是DoSysthesis过程。为了节省MMX提取图像源的不透明度分量的时间,我采取了预先计算出Alpha掩码表,在合并过程中进行地址引用的方式。Alpha掩码表存放在SysthesisAplhaMask变量中,在单元的初始化代码中对其进行初始化(代码参见《Delphi图像处理 -- 图像像素结构与图像数据转换》第八部分)。

下面是一个图像合成例子:

var Data: TImageData; bmp: TGpBitmap; g: TGpGraphics; begin bmp := TGpBitmap.Create('D:\xmas_011.png'); Data := GetImageData(TGpBitmap.Create('d:\56-3.jpg'), True); ImageSysthesis(Data, 0, 0, bmp); Bmp.Free; Bmp := ImageDataToGpBitmap(Data); g := TGpGraphics.Create(Canvas.Handle); g.DrawImage(Bmp, 0, 0); g.Free; Bmp.Free; FreeImageData(Data); end;

因手头没有好的图片,故随便找了2张演示了一下,由于图像源比目标图像高一些,所以图像合成过程对图像源进行了裁剪。也可调用拉伸合成过程将图像源进行缩放合成,但那使用的过程并不是本文要介绍的图像合成过程,而是图像缩放过程ImageScale了(见《Delphi图像处理 -- 图像缩放》)。你也可以使用GetSubImageData或者GetClipImageData过程(见《Delphi图像处理 -- 图像像素结构与图像数据转换》),将图像源的任意局部合成到目标图像。例子代码的合成效果图如下:

目标图像:目标图像

源 图 像:图像源

合成图像:合成效果图

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

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

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

maozefa@hotmail.com

说明:本文代码于2010.5.20重新修订过。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值