Delphi图像处理 -- 图像黑白调整

来源:互联网 发布:js单选alery 编辑:程序博客网 时间:2024/05/16 06:26

阅读提示

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

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

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

    本文代码必须包括《C++图像处理 -- 数据类型及公用函数》文章中的BmpData.h头文件。

 

    Photoshop CS的图像黑白调整功能,是通过对红、黄、绿、青、蓝和洋红等6种颜色的比例调节来完成的。能更精细地将彩色图片转换为高质量的黑白照片。

    Photoshop CS图像黑白调整功能的计算公式为:

    gray = (max - mid) * ratio_max + (mid - min) * ratio_max_mid + min

    公式中:gray为像素灰度值,max、mid和min分别为图像像素R、G、B分量颜色的最大值、中间值和最小值,ratio_max为max所代表的分量颜色(单色)比率,ratio_max_mid则为max与mid两种分量颜色所形成的复色比率。

    用上面公式计算的灰度值,与我们通常所用的灰度计算方法有很大不同,通常所用的灰度公式为,是直接将颜色各分量乘以相应的比率相加而成,如:gray = 0.3R + 0.59G + 0.11B,而上面公式则是在最小值代表的颜色分量基础上,用最大值与最小值之差表示单色部分(红、绿、蓝),用中间值与最小值之差表示复色部分(黄、青、洋红),将单色和复色部分分别乘以与之对应的比率后相加,再加上最小值而得到灰度值。对于每个单独的像素来说,计算灰度值只需要用到上述6种颜色比率中的2种即可。在计算过程中可根据像素RGB相互关系选择对应的单色和复色比率,如像素RGB的大小关系为R>G>B,单色比率选最大值R红色,复色比率则为最大值R与中间值G所形成的复色黄色。

    用程序代码实现上面的灰度计算公式并不复杂,难点还是前面所说的根据像素RGB相互关系选择对应的单色和复色比率

    Photoshop图像黑白调整功能中还有一个附加的着色功能,该功能实际上是利用图层颜色混合模式原理实现的。有关图层颜色混合模式的原理和实现在C++图像处理中有多篇文章进行了介绍,在《Delphi图像处理 -- 图像颜色混合》中也有实现代码,可以参考这些文章。

    下面是用Delphi实现图像黑白调整的BASM代码,包括灰度图象着色功能代码:

type  TBWParams = array[0..5] of Single;// eax,edx,ecx=r,g,b esi,sdi,ebx=rIndex,gIndex,bIndexprocedure CompareRgb;asm    cmp     eax, ecx    jae     @@1    xchg    eax, ecx    xchg    esi, ebx@@1:    cmp     eax, edx    jae     @@2    xchg    eax, edx    xchg    esi, edi@@2:    cmp     ecx, edx    jbe     @@3    xchg    ecx, edx    xchg    ebx, edi@@3:end;// in: esi=srcPixel,edi=dstPixel,eax=gray// out: [edi]=mixerColorprocedure ColorMix;const GrayConst: array[0..2] of Integer = (113, 604, 307);var  gray, max_min: LongWord;asm    push    esi    push    edi    mov     gray, eax    movzx   ecx, [esi].TARGBQuad.Blue    movzx   edx, [esi].TARGBQuad.Green    movzx   eax, [esi].TARGBQuad.Red    xor     ebx, ebx            // blue  index    mov     edi, 1              // green index    mov     esi, 2              // red   index    call    CompareRgb          // CompareRgb(red, green, blue)    sub     eax, ecx            // max - min    jnz     @@4    pop     edi    mov     eax, gray    mov     [edi].TARGBQuad.Blue, al    mov     [edi].TARGBQuad.Green, al    mov     [edi].TARGBQuad.Red, al    jmp     @@Exit@@4:    sub     edx, ecx            // mid - min    mov     max_min, eax    mov     ecx, eax    sub     eax, edx    imul    eax, GrayConst[edi*4].Integer    imul    ecx, GrayConst[ebx*4].Integer    add     eax, ecx    add     eax, 512            // nMax = gray +    shr     eax, 10             //   (max_min - mid_min) * grayConst[midIndex] +    add     eax, gray           //   max_min * grayConst[minIndex]    cmp     eax, 255    ja      @@5    mov     ecx, eax    sub     ecx, max_min        // nMin = nMax - max_min    js      @@6    add     edx, ecx            // nMid = nMin + mid_min    jmp     @@8@@5:// nMax > 255    shl     edx, 10             // hueCoef = (mid_min << 10) / max_min    mov     eax, max_min    xchg    eax, edx    mul     DivTab[edx*4].Integer    push    edx    mov     ecx, GrayConst[edi*4].Integer    imul    edx, ecx    shr     edx, 10             // v0 = (ys[mid.index] * hueCoef) >> 10    add     ecx, GrayConst[ebx*4].Integer    sub     ecx, edx            // v1 = ys[mid.index] + ys[min.index] - v0    add     edx, GrayConst[esi*4].Integer    mov     eax, edx    shl     edx, 8    sub     edx, eax    mov     eax, gray    shl     eax, 10    sub     eax, edx    mov     edx, ecx    shr     edx, 1    add     eax, edx    mul     DivTab[ecx*4].Integer    mov     ecx, edx            // nMin = ((gray << 10) - (ys[max.index] + v0) *    pop     eax                 //   255 + (v1 >> 1)) / v1    xor     edx, 255    imul    edx, eax    add     edx, 512    shr     edx, 10    add     edx, ecx            // nMid = nMin + (((255 ^ newMin) * hueCoef + 512) >> 10)    mov     eax, 255            // nMax = 255    jmp     @@8@@6:// nMin < 0    shl     edx, 10             // hueCoef = (mid_min << 10) / max_min    mov     eax, max_min    xchg    eax, edx    mul     DivTab[edx*4].Integer    push    edx    imul    edx, GrayConst[edi*4].Integer    add     edx, 512    shr     edx, 10             // tmp = ys[max.index] + ((ys[mid.index] * hueCoef + 512) >> 10)    add     edx, GrayConst[esi*4].Integer    mov     eax, gray    shl     eax, 10    mov     ecx, edx    shr     edx, 1    add     eax, edx    mul     DivTab[ecx*4].Integer    mov     eax, edx            // nMax = ((gray << 10) + (tmp >> 1)) / tmp    pop     edx    imul    edx, eax    add     edx, 512    shr     edx, 10             // nMid = (nMax * hueCoef + 512) >> 10    mov     ecx, 1              // nMin = 1@@8:    mov     ah, dl    pop     edx    mov     [edx+esi], al    mov     [edx+edi], ah    mov     [edx+ebx], cl    mov     edi, edx@@Exit:    pop     esiend;// 图像黑白调整。// 调整参数bwParams为元素数等于6的数组指针,分别为红,黄,绿,青,蓝,洋红procedure ImageBlackWhite(var Dest: TImageData; const Source: TImageData;  const bwParams: TBWParams); overload;var  i: Integer;  Width, Height: Integer;  dstOffset, srcOffset: Integer;  Params: array[0..5] of Integer;begin  for i := 0 to 5 do          // 浮点黑白参数转换为整数并交换青色与洋红色位置    Params[i] := Round(bwParams[i] * 1024);  Params[3] := Params[3] xor Params[5];  Params[5] := Params[5] xor Params[3];  Params[3] := Params[3] xor Params[5];  asm    push    esi    push    edi    push    ebx    mov     eax, Dest    mov     edx, Source    call    _SetCopyRegs    mov     Width, ecx    mov     Height, edx    mov     dstOffset, ebx    mov     srcOffset, eax@@yLoop:    push    Width@@xLoop:    push    esi    push    edi    movzx   ecx, [esi].TARGBQuad.Blue    movzx   edx, [esi].TARGBQuad.Green    movzx   eax, [esi].TARGBQuad.Red    mov     ebx, 4          // blue  index    mov     edi, 2          // green index    xor     esi, esi        // red   index    call    CompareRgb      // CompareRgb(red, green, blue)    sub     eax, edx        // max - mid    sub     edx, ecx        // mid - min    add     edi, esi    dec     edi    imul    eax, Params[esi*4].Integer    imul    edx, Params[edi*4].Integer    add     eax, edx        // gray = (((max - mid) * params[maxIndex] +    add     eax, 512        //          (mid - min) * params[maxIndex + midIndex - 1] +    sar     eax, 10         //           512) >> 10) + min    add     eax, ecx    jns     @@1    xor     eax, eax    jmp     @@2@@1:    cmp     eax, 255    jb      @@2    mov     eax, 255@@2:    pop     edi    pop     esi    mov     [edi].TARGBQuad.Blue, al    mov     [edi].TARGBQuad.Green, al    mov     [edi].TARGBQuad.Red, al    add     edi, 4    add     esi, 4    dec     Width    jne     @@xLoop    pop     Width    add     edi, dstOffset    add     esi, srcOffset    dec     Height    jnz     @@yLoop    pop     ebx    pop     edi    pop     esi  end;end;// 图像黑白调整。// 调整参数bwParams为元素数等于6的数组指针,分别为红,黄,绿,青,蓝,洋红procedure ImageBlackWhite(var Data: TImageData; const bwParams: TBWParams); overload;begin  ImageBlackWhite(Data, Data, bwParams);end;// 灰度图像染色。procedure ImageArgbTint(var Data: TImageData; Argb: LongWord);var  Width, Height: Integer;  dataOffset: Integer;  mixTable: array[0..255] of TARGBQuad;asm    push      esi    push      edi    push      ebx    push      eax    push      edx    // 建立灰度染色表mixTable    shr       edx, 24    cvtsi2ss  xmm6, edx    mov       edx, 255    cvtsi2ss  xmm0, edx    divss     xmm6, xmm0    pshufd    xmm6, xmm6, 0 // xmm6 = Argb.Alpha / 255    mov       edx, 1    movd      xmm5, edx    pshufd    xmm5, xmm5, 0 // xmm5 = 1    pxor      xmm4, xmm4    // xmm4 = 0    pxor      xmm7, xmm7    lea       esi, [esp]    lea       edi, mixTable    xor       eax, eax@@CalcMixTable:    push      eax    //   mixTable[i].rgb = i + ColorMix(&Argb, i) * Argb.Alpha / 255    call      ColorMix    movd      xmm0, [edi]   // xmm0 = ColorMix(&Argb, i)    punpcklbw xmm0, xmm7    punpcklwd xmm0, xmm7    psubd     xmm0, xmm4    // xmm0 -= i    cvtdq2ps  xmm0, xmm0    mulps     xmm0, xmm6    // xmm0 = xmm0 * Argb.Alpha / 255    cvtps2dq  xmm0, xmm0    paddd     xmm0, xmm4    // xmm0 += i    paddd     xmm4, xmm5    // i ++    packssdw  xmm0, xmm7    packuswb  xmm0, xmm7    movd      [edi], xmm0   // mixTable[i] = xmm0    pop       eax    add       edi, 4    inc       eax    cmp       eax, 256    jb        @@CalcMixTable    pop       edx    pop       eax    call      _SetDataRegs    lea       eax, mixTable@@yLoop:    push      ecx@@xLoop:    movzx     esi, [edi].TARGBQuad.Blue    lea       esi, [eax+esi*4]    movsw    movsb                   // pd.RGB = mixTable[pd.Blue]    inc       edi    loop      @@xLoop    pop       ecx    add       edi, ebx    dec       edx    jnz       @@yLoop    pop       ebx    pop       edi    pop       esiend;// 灰度图像染色。procedure ImageColorTint(var Data: TImageData; Color: TColor);asm    bswap     edx    shr       edx, 8    or        edx, 0ff000000h    call      ImageArgbTintend;

    代码中有2个图像黑白调整过程,分别是图像数据拷贝形式和自处理的黑白调整过程。染色过程也有2个,分别对应于ARGB颜色和VCL颜色TColor的,染色过程采用了颜色表查找方式处理,效率很高,即使用纯pascal代码处理速度也相当的快,如果不考虑颜色的不透明度,过程中的SSE代码可以删除。

    下面是个简单处理PNG图片黑白去色后染色的例子:

procedure TForm1.Button1Click(Sender: TObject);var  bmp: TGpBitmap;  g: TGpGraphics;  data: TImageData;begin  bmp := TGpBitmap.Create('..\..\media\xmas_011.png');  data := LockGpBitmap(bmp);  ImageBlackWhite(data, BWColors);  ImageArgbTint(data, $ffff0000);  UnlockGpBitmap(bmp, data);  g := TGpGraphics.Create(PaintBox1.Canvas.Handle);  g.DrawImage(bmp, 0, 0);  g.Free;  bmp.Free;end;

    运行截图如下:

    左边是原图,右边是黑白灰度处理后以红色着色。在这里有必要提示一下,染色过程处理的不只是黑白处理后的图像,任何灰度图象都可以的。

    本来写了一个仿Photoshop黑白调整功能的Delphi程序,但代码较长,而且在《C++图像处理 -- 图像黑白调整应用》一文中已经存在一个相同界面的演示程序,其中还有几张运行界面截图,所以这里就不再贴代码了。有兴趣的朋友可以参见该文。

 

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

    这里可访问《C++图像处理 -- 文章索引

原创粉丝点击