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

來源:互聯網
上載者:User

閱讀提示:

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

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

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

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

 

    最近一段時間,在整理以前的文章,前幾天剛剛將Delphi影像處理 -- 色相/飽和度調整一文修改整理完畢,可昨天不知怎麼突然想到其實Delphi影像處理 -- 色相/飽和度調整中的代碼還有最佳化的餘地,因為在我的幾篇關於映像色相/飽和度調整的文章裡,一直是按照SHL為基礎寫的代碼,事實上Photoshop的色相/飽和度調整功能也確實是以SHL為基礎的,但是畢竟Photoshop的色相/飽和度調整並不是SHL調整,無論是明度、色相還是飽和度都有著很大獨立性,它們之間的關係是鬆散性的,並非象SHL那樣,三者聯絡得很緊,在Delphi影像處理 -- 色相/飽和度調整一文中,明度部分已經是獨立調整的,但色相和飽和度部分還有著一些聯絡,主要是共用像素的S、L部分,但事實上,用SHL調整色相是很麻煩的,如果採用SHV來調整色相則會簡單得多(當然,這隻是指我採用的SSE代碼,如果不是SSE代碼,就很難說了,因為在Delphi影像處理 -- 色相/飽和度調整》。一文的色相調整代碼中,只計算了中間值,最大值和最小值保留原來的,這樣本身就節省了不少代碼)。改進代碼寫完後,本想覆蓋更新Delphi影像處理 -- 色相/飽和度調整中的代碼,但又覺得以前的代碼還是保留為好,因此,把這改進後的代碼作為續篇,以便二者之間對照比較:

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  _fc2: Single = 2.0;  _fc4: Single = 4.0;  _fc6: Single = 6.0;  _fc128: Single = 128.0;var  Hv0: Integer;  fHv: Single;  width, height, datOffset: Integer;asm    push      esi    push      edi    push      ebx    push      ecx    mov       Hv0, edx    call      _SetDataRegs    mov       width, ecx    mov       height, edx    mov       datOffset, ebx    pop       ebx             // Sv    pxor      xmm7, xmm7    pxor      xmm3, xmm3      // xmm3清零,便於色相處理時的或運算    mov       eax, 1    cvtsi2ss  xmm6, eax    mov       eax, 60    cvtsi2ss  xmm5, Hv0    cvtsi2ss  xmm4, eax    divss     xmm5, xmm4    movss     fHv, xmm5       // fHv = Hv / 60    mov       eax, 255    cvtsi2ss  xmm5, ebx    cvtsi2ss  xmm4, eax    divss     xmm5, xmm4    movss     xmm4, xmm5      // xmm4 = xmm5 = Sv / 255    test      ebx, ebx    jle       @@1    movaps    xmm5, xmm6      // if (Sv > 0)    subss     xmm5, xmm4      //   xmm5 = 1 / (1 - xmm4) - 1    rcpss     xmm5, xmm5    subss     xmm5, xmm6@@1:    pshufd    xmm5, xmm5, 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    jz        @@next          // if (delta == 0) continue    cvtsi2ss  xmm3, eax       // xmm3 = delta = rgbMax - rgbmin    cmp       Hv0, 0    jne       @@6    movd      xmm0, [edi]    punpcklbw xmm0, xmm7    punpcklwd xmm0, xmm7    cvtdq2ps  xmm0, xmm0    jmp       @@20@@6:    // 按HSV調整色相,比HSL快40%    movss     xmm2, fHv       // add = fHv    cmp       cl, [edi].TARGBQuad.Red    jne       @@8             // if (R == rgbMax) eax = G - B    movzx     eax, [edi].TARGBQuad.Green    movzx     esi, [edi].TARGBQuad.Blue    jmp       @@10@@8:    cmp       cl, [edi].TARGBQuad.Green    jne       @@9    movzx     eax, [edi].TARGBQuad.Blue    movzx     esi, [edi].TARGBQuad.Red    addss     xmm2, _fc2      // if (G == rgbMax) eax = B - R; add += 2    jmp       @@10@@9:    movzx     eax, [edi].TARGBQuad.Red    movzx     esi, [edi].TARGBQuad.Green    addss     xmm2, _fc4      // if (B == rgbMax) eax = R - G; add += 4@@10:    sub       eax, esi    cvtsi2ss  xmm1, eax    divss     xmm1, xmm3    addss     xmm1, xmm2      // 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  esi, xmm1       // index = Round(H)    cvtsi2ss  xmm2, esi    subss     xmm1, xmm2      // extra = H - index    comiss    xmm1, xmm7      // if (extra < 0) // 如果index發生五入    jae       @@13            // {    dec       esi             //   index --    addss     xmm1, xmm6      //   extra ++@@13:                         // }    test      esi, 1    jnz       @@14    movaps    xmm2, xmm1    movss     xmm1, xmm6    subss     xmm1, xmm2      // if ((index & 1) == 0) extra = 1 - extra@@14:    mulss     xmm1, xmm3      // xmm1 = delta * extra    pslldq    xmm1, 4    orps      xmm1, xmm3    movlhps   xmm1, xmm7      // xmm1 = 0  0  delta*extra  delta    cvtsi2ss  xmm0, ecx       // xmm0 = V = rgbMax    pshufd    xmm0, xmm0, 0   // xmm0 = V  V  V  V    subps     xmm0, xmm1      // xmm0 - xmm1 = NAN  V  T  P    jmp       @@jmpTable[esi*4].Pointer@@jmpTable:   dd  offset  @@H60              dd  offset  @@H120              dd  offset  @@H180              dd  offset  @@H240              dd  offset  @@H300              dd  offset  @@H360              dd  offset  @@H60// 當H=6.0時,SSE判斷誤差導致index=6,實際應為0@@H360:                       // 300 - 359 (V, P, T)    pshufd    xmm0, xmm0, 11100001b    jmp       @@H60@@H300:                       // 240 - 299 (T, P, V)    pshufd    xmm0, xmm0, 11010010b    jmp       @@H60@@H240:                       // 180 - 239 (P, T, V)    pshufd    xmm0, xmm0, 11000110b    jmp       @@H60@@H180:                       // 120 - 179 (P, V, T)    pshufd    xmm0, xmm0, 11001001b    jmp       @@H60@@H120:                       // 60 - 119  (T, V, P)    pshufd    xmm0, xmm0, 11011000b@@H60:                        // 0 - 59    (V, T, P)    test      ebx, ebx    je        @@25@@20:    // 調整飽和度。先按HSL計算亮度    add       ecx, edx        // ecx = rgbMar + rgbMin    cvtsi2ss  xmm2, ecx    divss     xmm2, _fc2      // xmm3 = L = ecx / 2    pshufd    xmm2, xmm2, 0    movaps    xmm1, xmm0    subps     xmm0, xmm2      // rgb0 = rgb - L    test      ebx, ebx    jle       @@23    // 如果飽和度增量為正,按SHL計算飽和度以控制新的飽和度上限    comiss    xmm2, _fc128    // if (Sv > 0)    jb        @@21            // {    neg       ecx    add       ecx, 510        //   if (L >= 128) ecx = 510 - ecx@@21:    cvtsi2ss  xmm2, ecx    divss     xmm3, xmm2      //   S = delta / ecx    addss     xmm3, xmm4    comiss    xmm3, xmm6    jb        @@23    subss     xmm3, xmm4      //   if ((xmm4 + S) >= 1)    rcpss     xmm2, xmm3      //     rgb0 = rgb0 * (1 / S - 1)    subss     xmm2, xmm6      //   else    pshufd    xmm2, xmm2, 0   //     // 事先已經計算好存放在xmm5中    mulps     xmm0, xmm2      //     rgb0 = rgb0 * (1 / (1 - xmm4) - 1)    jmp       @@24            // }@@23:                         // else    mulps     xmm0, xmm5      //   rgb0 = rgb0 * fSv@@24:    addps     xmm0, xmm1      // rgb += rgb0@@25:    cvtps2dq  xmm0, xmm0    packssdw  xmm0, xmm7    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;

    從上面的代碼結構看,色相、飽和度和明度三者基本“獨立”了,明度不用說,早獨立了,色相和飽和度之間共用的部分僅僅是像素RGB值的最大值和差值(HSBSetHueAndSaturation過程中的ecx和xmm3)。經過簡單測試,改進的代碼速度提升不少,色相、飽和度和明度三者全部調整的時間只相當於Delphi影像處理 -- 色相/飽和度調整中的色相部分的調整時間。

    最後鄙視一下CSDN,近來一直整理修改以前的文章,每一篇文章都重新加了4 - 5個標籤,可是沒過幾天,文章的標籤沒了,我以為是我沒設定好,又重新加了一遍,幾天后,又沒了!不帶這麼玩人的,再次鄙視CSDN。

 

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