700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Delphi图像处理 -- 图像合成

Delphi图像处理 -- 图像合成

时间:2023-05-31 13:52:46

相关推荐

Delphi图像处理 -- 图像合成

阅读提示:

Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。

C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。

尽可能保持二者内容一致,可相互对照。

本文代码必须包括文章《Delphi图像处理 -- 数据类型及公用过程》中的ImageData.pas单元。

在图像处理过程中,图像的合成操作是使用频率最高的,如图像显示、图像拷贝、图像拼接以及的图层拼合叠加等。

图像合成,其实也就是图像像素颜色的混合,在Photoshop中,颜色混合是个很复杂的东西,不同的混合模式,将产生不同的合成效果,如果将之全部研究透彻,估计就得写一本书。因此,本文只谈谈最基本的图像合成,也就是Photoshop中的正常混合模式。

只要接触过图像处理的,都知道有个图像像素混合公式:

1)dstRGB = srcRGB * alpha + dstRGB * (1 - alpha)

其中,dstRGB为目标图像素值;srcRGB为源图像素值;alpha为源图像素值混合比例(不透明度,范围0 - 1)。

其实,这个像素混合公式有很大局限性,只适合不含Alpha信息的图像。

要处理包括带Alpha通道图像(层)的混合,其完整的公式应该是:

2-1)srcRGB = srcRGB * srcAlpha * alpha / 255 (源图像素预乘转换为PARGB)

2-2)dstRGB = dstRGB * dstAlpha / 255 (目标图像素预乘转换为PARGB)

2-3)dstRGB = dstRGB + srcRGB - dstRGB * srcAlpha * alpha / 255 (源图像素值与目标图像素值混合)

2-4)dstAlpha = dstAlpha + srcAlpha * alpha - dstAlpha * srcAlpha * alpha / 255 (混合后的目标图Alpha通道值)

2-5)dstRGB = dstRGB * 255 / dstAlpha (混合后的目标图像素转换为ARGB)

其中,dstRGB为目标图像素值;srcRGB为源图像素值;dstAlpha为目标图Alpha通道值;srcAlpha为源图Alpha通道值;dstARGB为含Alpha目标图像素值;alpha为源图像素值混合比例(不透明度,范围0 - 1)。

将公式2中的2-1式代入2-3式,简化可得:

3-1)dstRGB = dstRGB * dstAlpha / 255

3-2)dstRGB = dstRGB + (srcRGB - dstRGB) * srcAlpha * alpha / 255

3-3)dstAlpha = dstAlpha + srcAlpha * alpha - dstAlpha * srcAlpha * alpha / 255

3-4)dstRGB = dstRGB * 255 / dstAlpha

当dstAlpha=srcAlpha=255时,公式3中3-1式、3-3式和3-4式没有意义,3-2式也变化为:

4)dstRGB = dstRGB + (srcRGB - dstRGB) * alpha

不难看出,公式4是公式1的变形。因此,公式1只是公式3(或者公式2)在目标图和源图都不含Alpha信息(或者Alpha=255)情况下的一个特例而已。

当公式4中的alpha=1时,目标图像素等于源图像素,所以,本文前面说图像拷贝其实也是图像合成的范畴。

通过上面较详细的分析,可以看出,即使是最基本正常图像混合模式也是很复杂的。其实,上面还不是完整的分析,因为按照目标图Alpha信息、源图Alpha信息以及源图合成比例等三个要素的完全的排列组合,最多可以派生8个公式。

下面就按正常混合模式的全部8种情况(有2项重合,实际为7种情况)来分别进行代码实现,也可完善和补充上面的文字叙述:

unit main;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls;typeTForm1 = class(TForm)Button1: TButton;Button2: TButton;procedure Button1Click(Sender: TObject);procedure Button2Click(Sender: TObject);private{ Private declarations }public{ Public declarations }end;varForm1: TForm1;implementationuses Gdiplus, ImageData, Jpeg;{$R *.dfm}procedure _SetMixerMM;asmpxor mm7, mm7mov eax, 1011hmovd mm6, eaxpshufwmm6, mm6, 0mov eax, 8movd mm5, eaxpshufwmm5, mm5, 0end;// --> mm7 4 * word = 0// --> mm6 4 * word = 0x1101// --> mm5 4 * word = 4// --> eax source alpha// --> esi source pixel (ARGB)// --> edi dest pixel (ARGB)// <-- eax dest alpha !!!// <-- mm0 dest pixelprocedure _PARGBMixer(srcAlpha: Integer);asmpushedxmovdmm0, [esi]movdmm1, [edi]punpcklbw mm0, mm7punpcklbw mm1, mm7// dest.rgb = dest.rgb * dest.alpha / 255movzxedx, [edi].TARGBQuad.Alphapmullw mm1, qword ptr ArgbTab[edx*8]pmulhuw mm1, mm6paddusw mm1, mm5psrlwmm1, 4// dest.rgb = (dest.rgb * 255 + (source.rgb - dest.rgb) * sourec.alpha) / 255psubwmm0, mm1pmullw mm0, qword ptr ArgbTab[eax*8]pmullw mm1, qword ptr ArgbTab[255*8]paddwmm0, mm1pmulhuw mm0, mm6paddusw mm0, mm5psrlwmm0, 4// dest.alpha += (source.alpha - (dest.alpha * source.alpha + 127) / 255)pusheaxadd [esp], edximuleax, edxadd eax, 127mul dword ptr DivTab[255*4]pop eaxsub eax, edx// dest.rgb = dest.rgb * 255 / dest.alphamovqmm1, mm0psllwmm0, 8psubwmm0, mm1pmulhuw mm0, qword ptr MMDivTab[eax*8]packuswb mm0, mm7pop edxend;// --> mm7 4 * word = 0// --> eax source alpha// --> esi source pixel (ARGB)// --> edi dest pixel (ARGB)// <-- mm0 dest pixel (RGB)procedure _ARGBMixer(srcAlpha: Integer);asmmovdmm0, [esi]movdmm1, [edi]punpcklbw mm0, mm7punpcklbw mm1, mm7psubwmm0, mm1pmullw mm0, qword ptr ArgbTab[eax*8]psllwmm1, 8paddwmm0, mm1psrlwmm0, 8packuswb mm0, mm0end;procedure _DoMixer(var Dest: TImageData; const Source: TImageData; Alpha: Integer);vardstOffset, srcOffset:Integer;alphaI: Integer;dst: PImageData;asmpushesipushedipushebxmov dst, eaxmov alphaI, ecxshr ecx, 8movzxebx, [eax].TImageData.AlphaFlagshl ebx, 1or ecx, ebxmovzxebx, [edx].TImageData.AlphaFlagshl ebx, 2or ecx, ebxpushecxcall_SetCopyRegsmov srcOffset, eaxpop eaxjmp dword ptr [@@jmpTable+eax*4]@@jmpTable:dd @@mixer0, @@mixer1, @@mixer2, @@mixer3, @@mixer4, @@mixer5, @@mixer6, @@mixer7// src = 0, dst = 0, alpha = 0@@mixer0:mov eax, alphaImovqmm2, qword ptr ArgbTab[eax*8]mov eax, srcOffsetpxormm7, mm7@@yLoop0:pushecx@@xLoop0:movdmm0, [esi]movdmm1, [edi]punpcklbw mm0, mm7punpcklbw mm1, mm7psubwmm0, mm1pmullw mm0, mm2psllwmm1, 8paddwmm0, mm1psrlwmm0, 8packuswb mm0, mm0movd[edi], mm0mov [edi].TARGBQuad.Alpha, 255add esi, 4add edi, 4loop@@xLoop0add esi, eaxadd edi, ebxpop ecxdec edxjnz @@yLoop0jmp @@End// src = 0, dst = 0, alpha = 1@@mixer1:// src = 0, dst = 1, alpha = 1@@mixer3:mov eax, srcOffset@@yLoop1:pushecxrep movsdpop ecxadd esi, eaxadd edi, ebxdec edxjnz @@yLoop1jmp @@Endmov eax, dstmov [eax].TImageData.AlphaFlag, Falsejmp @@Exit// src = 0, dst = 1, alpha = 0@@mixer2:call_SetMixerMMmov eax, alphaIimuleax, 255shr eax, 8@@yLoop2:pushecx@@xLoop2:pusheaxcall_PARGBMixermovd[edi], mm0mov [edi].TARGBQuad.Alpha, alpop eaxadd esi, 4add edi, 4loop@@xLoop2add esi, srcOffsetadd edi, ebxpop ecxdec edxjnz @@yLoop2jmp @@End// src = 1, dst = 0, alpha = 0@@mixer4:mov dstOffset, ebxmov ebx, alphaIpxormm7, mm7@@yLoop4:pushecx@@xLoop4:movzxeax, [esi].TARGBQuad.Alphaimuleax, ebxshr eax, 8jz @@Next4call_ARGBMixermovd[edi], mm0mov [edi].TARGBQuad.Alpha, 255@@Next4:add esi, 4add edi, 4loop@@xLoop4add esi, srcOffsetadd edi, dstOffsetpop ecxdec edxjnz @@yLoop4jmp @@End// src = 1, dst = 0, alpha = 1@@mixer5:pxormm7, mm7@@yLoop5:pushecx@@xLoop5:movzxeax, [esi].TARGBQuad.Alphacall_ARGBMixermovd[edi], mm0mov [edi].TARGBQuad.Alpha, 255add esi, 4add edi, 4loop@@xLoop5add esi, srcOffsetadd edi, ebxpop ecxdec edxjnz @@yLoop5jmp @@End// src = 1, dst = 1, alpha = 0@@mixer6:mov dstOffset, ebxmov ebx, alphaIcall_SetMixerMM@@yLoop6:pushecx@@xLoop6:movzxeax, [esi].TARGBQuad.Alphaimuleax, ebxshr eax, 8jz @@Next6call_PARGBMixermovd[edi], mm0mov [edi].TARGBQuad.Alpha, al@@Next6:add esi, 4add edi, 4loop@@xLoop6add esi, srcOffsetadd edi, dstOffsetpop ecxdec edxjnz @@yLoop6jmp @@End// src = 1, dst = 1, alpha = 1@@mixer7:call_SetMixerMM@@yLoop7:pushecx@@xLoop7:movzxeax, [esi].TARGBQuad.Alphacall_PARGBMixermovd[edi], mm0mov [edi].TARGBQuad.Alpha, aladd esi, 4add edi, 4loop@@xLoop7add esi, srcOffsetadd edi, ebxpop ecxdec edxjnz @@yLoop7@@End:emms@@Exit:pop ebxpop edipop esiend;procedure ImageMixer(var Dest: TImageData; const Source: TImageData; Alpha: Single);varalphaI: Integer;beginalphaI := Round(Alpha * 256);if alphaI < 0 then Exit;if alphaI > 256 then alphaI := 256;_DoMixer(Dest, Source, alphaI);end;procedure TForm1.Button1Click(Sender: TObject);varsource, dest: TGpBitmap;g: TGpGraphics;src, dst: TImageData;beginsource := TGpBitmap.Create('..\..\media\Apple.png');dest := TGpBitmap.Create('..\..\media\xmas_011.png');g := TGpGraphics.Create(Canvas.Handle);g.DrawImage(dest, 0, 0);g.DrawImage(source, dest.Width, 0);src := LockGpBitmap(source);dst := LockGpBitmap(dest);ImageMixer(dst, src, 0.75);UnlockGpBitmap(dest, dst);UnlockGpBitmap(source, src);g.DrawImage(dest, dst.Width + src.Width, 0);g.Free;dest.Free;source.Free;end;procedure TForm1.Button2Click(Sender: TObject);varsource: TGpBitmap;dest: TBitmap;tmp: TJpegImage;g: TGpGraphics;src, dst: TImageData;begindest := TBitmap.Create;tmp := TJpegImage.Create;tmp.LoadFromFile('..\..\media\IMG_9440_mf.jpg');dest.Assign(tmp);tmp.Free;source := TGpBitmap.Create('..\..\media\xmas_011.png');g := TGpGraphics.Create(Canvas.Handle);Canvas.Draw(0, 0, dest);g.DrawImage(source, dest.Width, 0);dst := GetBitmapData(dest);src := LockGpBitmap(source);ImageMixer(dst, src, 1);UnlockGpBitmap(source, src);Canvas.Draw(dst.Width + src.Width, 0, dest);g.Free;dest.Free;source.Free;end;end.

上面是一个完整Delphi程序,在_DoMixer过程中,定义了7个图像合成子过程,以满足全部8种排列组合的图像合并。除了@@Mixer0和@@Mixer1子过程外,其余子过程分别调用了_ARGBMixer和_PARGBMixer过程,其实@@Mixer0中的处理代码与_ARGBMixer过程基本是相同的,也就是说,7个子过程分别使用了三种处理方式:

一是@@Mixer1的完全拷贝方式;

二是目标图不含Alpha信息,将源图按像素的Alpha信息或者给定的不透明度混合到目标图的_ARGBMixer过程,该过程使用的是前面公式1或公式4的简化公式。

三是目标图为带Alpha信息的处理形式_PARGBMixer,该过程使用前面的公式3。

如果嫌7个子过程太多,也可简化代码,只保留满足上述3种情况的子过程,即@@Mixer1、@@Mixer4和@@Mixer6,对处理效率影响不大。

上面程序中包含2个例子,前一个是用GDI+位图打开2张png图片后进行处理,这个处理调用的是@@Mixer6。运行效果截图如下:

后一个例子是对GDI+位图和VCL位图的混合处理,其中VCL位图是JPEG格式文件,这是没有Alpha信息的,这个例子运行效果截图如下:

《Delphi图像处理》系列使用GDI+单元下载地址和说明见文章《GDI+ for VCL基础 -- GDI+ 与 VCL》。

因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@

这里可访问《Delphi图像处理 -- 文章索引》。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。