Delphi影像處理 — 映像合成

來源:互聯網
上載者:User

閱讀提示:

    《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;interfaceuses  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  Dialogs, StdCtrls;type  TForm1 = class(TForm)    Button1: TButton;    Button2: TButton;    procedure Button1Click(Sender: TObject);    procedure Button2Click(Sender: TObject);  private    { Private declarations }  public    { Public declarations }  end;var  Form1: TForm1;implementationuses Gdiplus, ImageData, Jpeg;{$R *.dfm}procedure _SetMixerMM;asm    pxor        mm7, mm7    mov         eax, 1011h    movd        mm6, eax    pshufw      mm6, mm6, 0    mov         eax, 8    movd        mm5, eax    pshufw      mm5, 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);asm    push      edx    movd      mm0, [esi]    movd      mm1, [edi]    punpcklbw mm0, mm7    punpcklbw mm1, mm7    // dest.rgb = dest.rgb * dest.alpha / 255    movzx     edx, [edi].TARGBQuad.Alpha    pmullw    mm1, qword ptr ArgbTab[edx*8]    pmulhuw   mm1, mm6    paddusw   mm1, mm5    psrlw     mm1, 4    // dest.rgb = (dest.rgb * 255 + (source.rgb - dest.rgb) * sourec.alpha) / 255    psubw     mm0, mm1    pmullw    mm0, qword ptr ArgbTab[eax*8]    pmullw    mm1, qword ptr ArgbTab[255*8]    paddw     mm0, mm1    pmulhuw   mm0, mm6    paddusw   mm0, mm5    psrlw     mm0, 4    // dest.alpha += (source.alpha - (dest.alpha * source.alpha + 127) / 255)    push      eax    add       [esp], edx    imul      eax, edx    add       eax, 127    mul       dword ptr DivTab[255*4]    pop       eax    sub       eax, edx    // dest.rgb = dest.rgb * 255 / dest.alpha    movq      mm1, mm0    psllw     mm0, 8    psubw     mm0, mm1    pmulhuw   mm0, qword ptr MMDivTab[eax*8]    packuswb  mm0, mm7    pop       edxend;// --> mm7 4 * word = 0// --> eax source alpha// --> esi source pixel (ARGB)// --> edi dest pixel (ARGB)// <-- mm0 dest pixel (RGB)procedure _ARGBMixer(srcAlpha: Integer);asm    movd      mm0, [esi]    movd      mm1, [edi]    punpcklbw mm0, mm7    punpcklbw mm1, mm7    psubw     mm0, mm1    pmullw    mm0, qword ptr ArgbTab[eax*8]    psllw     mm1, 8    paddw     mm0, mm1    psrlw     mm0, 8    packuswb  mm0, mm0end;procedure _DoMixer(var Dest: TImageData; const Source: TImageData; Alpha: Integer);var  dstOffset, srcOffset:Integer;  alphaI: Integer;  dst: PImageData;asm    push      esi    push      edi    push      ebx    mov       dst, eax    mov       alphaI, ecx    shr       ecx, 8    movzx     ebx, [eax].TImageData.AlphaFlag    shl       ebx, 1    or        ecx, ebx    movzx     ebx, [edx].TImageData.AlphaFlag    shl       ebx, 2    or        ecx, ebx    push      ecx    call      _SetCopyRegs    mov       srcOffset, eax    pop       eax    jmp       dword ptr [@@jmpTable+eax*4]@@jmpTable:    dd @@mixer0, @@mixer1, @@mixer2, @@mixer3, @@mixer4, @@mixer5, @@mixer6, @@mixer7// src = 0, dst = 0, alpha = 0@@mixer0:    mov       eax, alphaI    movq      mm2, qword ptr ArgbTab[eax*8]    mov       eax, srcOffset    pxor      mm7, mm7@@yLoop0:    push      ecx@@xLoop0:    movd      mm0, [esi]    movd      mm1, [edi]    punpcklbw mm0, mm7    punpcklbw mm1, mm7    psubw     mm0, mm1    pmullw    mm0, mm2    psllw     mm1, 8    paddw     mm0, mm1    psrlw     mm0, 8    packuswb  mm0, mm0    movd      [edi], mm0    mov       [edi].TARGBQuad.Alpha, 255    add       esi, 4    add       edi, 4    loop      @@xLoop0    add       esi, eax    add       edi, ebx    pop       ecx    dec       edx    jnz       @@yLoop0    jmp       @@End// src = 0, dst = 0, alpha = 1@@mixer1:// src = 0, dst = 1, alpha = 1@@mixer3:    mov       eax, srcOffset@@yLoop1:    push      ecx    rep       movsd    pop       ecx    add       esi, eax    add       edi, ebx    dec       edx    jnz       @@yLoop1    jmp       @@End    mov       eax, dst    mov       [eax].TImageData.AlphaFlag, False    jmp       @@Exit// src = 0, dst = 1, alpha = 0@@mixer2:    call      _SetMixerMM    mov       eax, alphaI    imul      eax, 255    shr       eax, 8@@yLoop2:    push      ecx@@xLoop2:    push      eax    call      _PARGBMixer    movd      [edi], mm0    mov       [edi].TARGBQuad.Alpha, al    pop       eax    add       esi, 4    add       edi, 4    loop      @@xLoop2    add       esi, srcOffset    add       edi, ebx    pop       ecx    dec       edx    jnz       @@yLoop2    jmp       @@End// src = 1, dst = 0, alpha = 0@@mixer4:    mov       dstOffset, ebx    mov       ebx, alphaI    pxor      mm7, mm7@@yLoop4:    push      ecx@@xLoop4:    movzx     eax, [esi].TARGBQuad.Alpha    imul      eax, ebx    shr       eax, 8    jz        @@Next4    call      _ARGBMixer    movd      [edi], mm0    mov       [edi].TARGBQuad.Alpha, 255@@Next4:    add       esi, 4    add       edi, 4    loop      @@xLoop4    add       esi, srcOffset    add       edi, dstOffset    pop       ecx    dec       edx    jnz       @@yLoop4    jmp       @@End// src = 1, dst = 0, alpha = 1@@mixer5:    pxor      mm7, mm7@@yLoop5:    push      ecx@@xLoop5:    movzx     eax, [esi].TARGBQuad.Alpha    call      _ARGBMixer    movd      [edi], mm0    mov       [edi].TARGBQuad.Alpha, 255    add       esi, 4    add       edi, 4    loop      @@xLoop5    add       esi, srcOffset    add       edi, ebx    pop       ecx    dec       edx    jnz       @@yLoop5    jmp       @@End// src = 1, dst = 1, alpha = 0@@mixer6:    mov       dstOffset, ebx    mov       ebx, alphaI    call      _SetMixerMM@@yLoop6:    push      ecx@@xLoop6:    movzx     eax, [esi].TARGBQuad.Alpha    imul      eax, ebx    shr       eax, 8    jz        @@Next6    call      _PARGBMixer    movd      [edi], mm0    mov       [edi].TARGBQuad.Alpha, al@@Next6:    add       esi, 4    add       edi, 4    loop      @@xLoop6    add       esi, srcOffset    add       edi, dstOffset    pop       ecx    dec       edx    jnz       @@yLoop6    jmp       @@End// src = 1, dst = 1, alpha = 1@@mixer7:    call      _SetMixerMM@@yLoop7:    push      ecx@@xLoop7:    movzx     eax, [esi].TARGBQuad.Alpha    call      _PARGBMixer    movd      [edi], mm0    mov       [edi].TARGBQuad.Alpha, al    add       esi, 4    add       edi, 4    loop      @@xLoop7    add       esi, srcOffset    add       edi, ebx    pop       ecx    dec       edx    jnz       @@yLoop7@@End:    emms@@Exit:    pop       ebx    pop       edi    pop       esiend;procedure ImageMixer(var Dest: TImageData; const Source: TImageData; Alpha: Single);var  alphaI: Integer;begin  alphaI := Round(Alpha * 256);  if alphaI < 0 then Exit;  if alphaI > 256 then alphaI := 256;  _DoMixer(Dest, Source, alphaI);end;procedure TForm1.Button1Click(Sender: TObject);var  source, dest: TGpBitmap;  g: TGpGraphics;  src, dst: TImageData;begin  source := 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);var  source: TGpBitmap;  dest: TBitmap;  tmp: TJpegImage;  g: TGpGraphics;  src, dst: TImageData;begin  dest := 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@hotmail.com

    這裡可訪問《Delphi影像處理 -- 文章索引》。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.