Delphi影像處理 — 色相/飽和度調整

來源:互聯網
上載者:User

閱讀提示:

    《Delphi影像處理》系列以效率為側重點,一般代碼為PASCAL,核心代碼採用BASM。

    《C++影像處理》系列以代碼清晰,可讀性為主,全部使用C++代碼。

    儘可能保持二者內容一致,可相互對照。

    本文代碼必須包括文章《Delphi影像處理 -- 資料類型及公用過程》中的ImageData.pas單元。

 

    本文是基於《GDI+在Delphi程式的應用 – Photoshop色相/飽和度/明度功能》一文的BASM實用性過程,有關實現原理可參見《GDI+ 在Delphi程式的應用 -- 映像飽和度調整》和《GDI+ 在Delphi程式的應用 -- 仿Photoshop的明度調整》,純PAS實現代碼和測試例子代碼見《GDI+在Delphi程式的應用 – Photoshop色相/飽和度/明度功能》。

procedure GetBrightTable(Bright: Integer; var Table: TGrayTable);asm    push    ebx    cmp     eax, -255    jge     @@1    mov     eax, -255    jmp     @@2@@1:    cmp     eax, 255    jle     @@2    mov     eax, 255@@2:    push    eax    mov     ebx, 255    fild    dword ptr[esp]    fwait    mov     [esp], ebx    fidiv   dword ptr[esp]// Bright / 255    fwait    xor     ecx, ecx    test    eax, eax    jg      @@Loop    xor     ebx, ebx      // mask = Bright > 0? 255 : 0@@Loop:    mov     [esp], ecx    xor     [esp], ebx    fild    dword ptr[esp]    fmul    st(0), st(1)    fistp   dword ptr[esp]    fwait    mov     eax, [esp]    add     eax, ecx    mov     [edx], al    // Table[i] = (i ^ mask) * Bright / 255    inc     edx    inc     ecx    cmp     ecx, 256    jb      @@Loop    ffree   st    pop     eax    pop     ebxend;procedure HSBSetBright(var Data: TImageData; const Table: TGrayTable);asm    push    ebp    push    esi    push    edi    push    ebx    mov     esi, edx    call    _SetDataRegs    mov     ebp, edx@@yLoop:    push    ecx@@xLoop:    movzx   eax, [edi].TARGBQuad.Blue    movzx   edx, [edi].TARGBQuad.Green    mov     al, [esi+eax]    mov     dl, [esi+edx]    mov     [edi].TARGBQuad.Blue, al    mov     [edi].TARGBQuad.Green, dl    movzx   eax, [edi].TARGBQuad.Red    mov     al, [esi+eax]    mov     [edi].TARGBQuad.Red, al    add     edi, 4    loop    @@xLoop    pop     ecx    add     edi, ebx    dec     ebp    jnz     @@yLoop    pop     ebx    pop     edi    pop     esi    pop     ebpend;procedure HSBSetHueAndSaturation(var Data: TImageData; Hv, Sv: Integer);const  _fcd5: Single = 0.5;  _fc1: Single = 1.0;  _fc2: Single = 2.0;  _fc4: Single = 4.0;  _fc6: Single = 6.0;  _fc255: Single = 255.0;  _fc510: Single = 510.0;var  fHv, fSv: Single;  width, height, datOffset: Integer;asm    push      esi    push      edi    push      ebx    push      edx    push      ecx    call      _SetDataRegs    mov       width, ecx    mov       height, edx    mov       datOffset, ebx    pop       ebx             // Sv    pop       esi             // Hv    pxor      xmm7, xmm7    mov       eax, 60    cvtsi2ss  xmm6, esi    cvtsi2ss  xmm0, eax    divss     xmm6, xmm0    movss     fHv, xmm6       // fSv = Hv / 60    cvtsi2ss  xmm6, ebx    divss     xmm6, _fc255    movss     fSv, xmm6       // xmm6 = fSv = Sv / 255    test      ebx, ebx    jle       @@1    mov       eax, 255    xor       eax, ebx    cvtsi2ss  xmm0, eax    movss     xmm6, _fc255    divss     xmm6, xmm0      // if (Sv > 0)    subss     xmm6, _fc1      //   xmm6 = 255 / (255 - Sv) - 1@@1:    pshufd    xmm6, xmm6, 0@@yLoop:    push      width@@xLoop:    movzx     ecx, [edi].TARGBQuad.Blue    movzx     edx, [edi].TARGBQuad.Green    movzx     eax, [edi].TARGBQuad.Red    cmp       ecx, edx        // ecx = rgbMax    jge       @@3             // edx = rgbMin    xchg      ecx, edx@@3:    cmp       ecx, eax    jge       @@4    xchg      ecx, eax@@4:    cmp       edx, eax    cmova     edx, eax    mov       eax, ecx    sub       eax, edx        // delta = rgbMax - rgbmin    jz        @@next          // if (delta == 0) continue@@5:    movd      xmm0, edx       // rgbMin    pinsrw    xmm0, ecx, 2    // rgbMax    add       ecx, edx        // ecx = rgbMar + rgbMin    cvtsi2ss  xmm3, ecx    divss     xmm3, _fc510    // xmm3 = L = ecx / 510    comiss    xmm3, _fcd5    jb        @@6    neg       ecx    add       ecx, 510        // if (L >= 0.5) ecx = 510 - ecx@@6:    cvtsi2ss  xmm2, eax    cvtsi2ss  xmm4, ecx    divss     xmm2, xmm4      // xmm2 = S = delta / ecx    test      esi, esi    jnz       @@7             // if (Hv == 0) goto @@20    movd      xmm0, [edi]    punpcklbw xmm0, xmm7    jmp       @@20@@7:    cvtsi2ss  xmm4, eax       // delta    movss     xmm5, fHv       // add = fHv    pextrw    eax, xmm0, 2    // rgbMax    cmp       al, [edi].TARGBQuad.Red    jne       @@8             // if (R == rgbMax) eax = G - B    movzx     eax, [edi].TARGBQuad.Green    movzx     edx, [edi].TARGBQuad.Blue    jmp       @@10@@8:    cmp       al, [edi].TARGBQuad.Green    jne       @@9    movzx     eax, [edi].TARGBQuad.Blue    movzx     edx, [edi].TARGBQuad.Red    addss     xmm5, _fc2      // if (G == rgbMax) eax = B - R; add += 2    jmp       @@10@@9:    movzx     eax, [edi].TARGBQuad.Red    movzx     edx, [edi].TARGBQuad.Green    addss     xmm5, _fc4      // if (B == rgbMax) eax = R - G; add += 4@@10:    sub       eax, edx    cvtsi2ss  xmm1, eax    divss     xmm1, xmm4    addss     xmm1, xmm5      // H = eax / delta + add    comiss    xmm1, xmm7    jae       @@11    addss     xmm1, _fc6      // if (H < 0) H += 6    jmp       @@12@@11:    comiss    xmm1, _fc6    jb        @@12    subss     xmm1, _fc6      // else if (H >= 6) H -= 6@@12:    cvtss2si  edx, xmm1       // index = Round(H)    cvtsi2ss  xmm4, edx    subss     xmm1, xmm4      // extra = H - index    comiss    xmm1, xmm7      // if (extra < 0) // 如果index發生五入    jae       @@13            // {    dec       edx             //   index --    addss     xmm1, _fc1      //   extra ++@@13:                         // }    test      edx, 1    jz        @@14    movaps    xmm4, xmm1    movss     xmm1, _fc1    subss     xmm1, xmm4      // if (index & 1) extra = 1 - extra@@14:    movaps    xmm4, xmm1    movss     xmm5, _fc1    subss     xmm4, _fcd5    subss     xmm5, xmm2    mulss     xmm4, xmm5    subss     xmm1, xmm4      // extra = extra - (extra - 0.5) * (1.0 - S);    movaps    xmm4, xmm1      // extra0 = extra    movaps    xmm5, xmm3    subss     xmm5, _fcd5     // L0 = L - 0.5    comiss    xmm5, xmm7    jb        @@15    movss     xmm4, _fc1    subss     xmm4, xmm1      // if (L0 >= 0) extra0 = 1 - extra@@15:    mulss     xmm4, xmm5    mulss     xmm4, _fc2    addss     xmm1, xmm4    mulss     xmm1, _fc255    cvtss2si  eax, xmm1       // rgbMid = (extra + extra0 * L0 * 2) * 255    pinsrw    xmm0, eax, 1    // xmm0 = 0000 MAX MEDIAN MIN    jmp       @@jmpTable[edx*4].Pointer@@jmpTable:   dd  offset  @@H60              dd  offset  @@H120              dd  offset  @@H180              dd  offset  @@H240              dd  offset  @@H300              dd  offset  @@H360              dd  offset  @@18// 當H=6.0時,SSE判斷誤差導致index=6,實際應為0@@H120:                       // 60 - 119    pshuflw   xmm0, xmm0, 216    jmp       @@18@@H180:                       // 120 - 179    pshuflw   xmm0, xmm0, 201    jmp       @@18@@H240:                       // 180 - 239    pshuflw   xmm0, xmm0, 198    jmp       @@18@@H300:                       // 240 - 299    pshuflw   xmm0, xmm0, 210    jmp       @@18@@H360:                       // 300 - 359    pshuflw   xmm0, xmm0, 225@@H60:                        // 0 - 59@@18:    test      ebx, ebx        // if (Sv == 0) continue    jz        @@25@@20:    punpcklwd xmm0, xmm7    cvtdq2ps  xmm0, xmm0    movaps    xmm1, xmm0    mulss     xmm3, _fc255    pshufd    xmm3, xmm3, 0    subps     xmm0, xmm3      // rgb0 = rgb - L    test      ebx, ebx    jle       @@21    movaps    xmm3, xmm2      // if (Sv > 0)    addss     xmm3, fSv       // {    comiss    xmm3, _fc1    jb        @@21    rcpss     xmm2, xmm2      //   if ((fSv + S) >= 1)    subss     xmm2, _fc1      //     rgb0 = rgb0 * (1 / S - 1)    pshufd    xmm2, xmm2, 0   //   else    mulps     xmm0, xmm2      //     rgb0 = rgb0 * (1 / (1 - fSv) - 1)    jmp       @@22            // }@@21:                         // else    mulps     xmm0, xmm6      //   rgb0 = rgb0 * fSv@@22:    addps     xmm0, xmm1      // rgb += rgb0    cvtps2dq  xmm0, xmm0    packssdw  xmm0, xmm7@@25:    packuswb  xmm0, xmm7    mov       al, [edi].TARGBQuad.Alpha    movd      [edi], xmm0    mov       [edi].TARGBQuad.Alpha, al@@next:    add       edi, 4    dec       width    jnz       @@xLoop    add       edi, datOffset    pop       width    dec       height    jnz       @@yLoop    pop       ebx    pop       edi    pop       esiend;procedure ImageHSBAdjustment(var Data: TImageData; hValue, sValue, bValue: Integer);var  BrightTab: TGrayTable;begin  if hValue > 180 then hValue := 180  else if hValue < -180 then hValue := -180;  if sValue > 255 then sValue := 255  else if sValue < -255 then sValue := -255;  if bValue <> 0 then GetBrightTable(bValue, BrightTab);  if (sValue > 0) and (bValue <> 0) then    HSBSetBright(Data, BrightTab);  if (hValue <> 0) or (sValue <> 0) then  begin    HSBSetHueAndSaturation(Data, hValue, sValue);  end;  if (sValue <= 0) and (bValue <> 0) then    HSBSetBright(Data, BrightTab);end;

    這次重新修訂,將明度部分獨立出來,採用一個256元素大小的明度表直接進行替換,大大加快了速度;色相和飽和度調整部分也進行了修改,全部採用SSE代碼,使計算精確度得以提高。

    下面是個簡單的HSB示範程式:

unit main;interfaceuses  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  Dialogs, Gdiplus, ImageData, StdCtrls, ComCtrls, ExtCtrls;type  TForm1 = class(TForm)    Label1: TLabel;    Label2: TLabel;    Label3: TLabel;    PaintBox1: TPaintBox;    Button1: TButton;    HBar: TTrackBar;    SBar: TTrackBar;    BBar: TTrackBar;    HEdit: TEdit;    SEdit: TEdit;    BEdit: TEdit;    Button2: TButton;    procedure FormCreate(Sender: TObject);    procedure FormDestroy(Sender: TObject);    procedure PaintBox1Paint(Sender: TObject);    procedure HBarChange(Sender: TObject);    procedure SBarChange(Sender: TObject);    procedure BBarChange(Sender: TObject);    procedure HEditChange(Sender: TObject);    procedure HEditKeyPress(Sender: TObject; var Key: Char);    procedure Button1Click(Sender: TObject);    procedure Button2Click(Sender: TObject);  private    { Private declarations }    Bmp: TGpBitmap;    tmpBmp: TGpBitmap;    r: TGpRect;    Lock: Boolean;  public    { Public declarations }  end;var  Form1: TForm1;implementation{$R *.dfm}procedure TForm1.BBarChange(Sender: TObject);begin  if not Lock then    BEdit.Text := IntToStr(BBar.Position);end;procedure TForm1.Button1Click(Sender: TObject);begin  HBar.Position := 0;  SBar.Position := 0;  BBar.Position := 0;end;procedure TForm1.FormCreate(Sender: TObject);begin  Bmp := TGpBitmap.Create('..\..\media\100_0349.jpg');  r := GpRect(0, 0, Bmp.Width, Bmp.Height);  tmpBmp := Bmp.Clone(r, pf32bppARGB);end;procedure TForm1.FormDestroy(Sender: TObject);begin  tmpBmp.Free;  Bmp.Free;end;procedure TForm1.HBarChange(Sender: TObject);begin  if not Lock then    HEdit.Text := IntToStr(HBar.Position);end;procedure TForm1.HEditChange(Sender: TObject);var  Data: TImageData;begin  Lock := True;  with TEdit(Sender) do  begin    if Length(Text) = 0 then Text := '0';    case Tag of      0: HBar.Position := StrToInt(Text);      1: SBar.Position := StrToInt(Text);      2: BBar.Position := StrToInt(Text);    end;    Lock := False;    tmpBmp.Free;    tmpBmp := Bmp.Clone(r, pf32bppARGB);    if (HBar.Position <> 0) or (SBar.Position <> 0) or (BBar.Position <> 0) then    begin      Data := LockGpBitmap(tmpBmp);      ImageHSBAdjustment(Data, HBar.Position, Round(SBar.Position * 255.0 / 100),        Round(BBar.Position * 255.0 / 100));      UnlockGpBitmap(tmpBmp, Data);    end;    PaintBox1Paint(nil);  end;end;procedure TForm1.HEditKeyPress(Sender: TObject; var Key: Char);begin  if (Key >= #32) and not (Key in ['0'..'9']) then    Key := #0;end;procedure TForm1.PaintBox1Paint(Sender: TObject);var  g: TGpGraphics;begin  g := TGpGraphics.Create(PaintBox1.Canvas.Handle);  try    g.DrawImage(tmpBmp, r);    g.TranslateTransform(0, r.Height);    g.DrawImage(Bmp, r);  finally    g.Free;  end;end;procedure TForm1.SBarChange(Sender: TObject);begin  if not Lock then    SEdit.Text := IntToStr(SBar.Position);end;end.

    運行介面如下:

 

    《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.