Delphi图像处理 -- 亮度/对比度调整

阅读提示:

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

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

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

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

  

        本人已经写过几篇关于亮度调整的文章,但是关于图像的对比度调整的过程和文章却一直没有写,其原因是一直没找到一个好的算法。可能有人会说,图像的亮度,对比度调整是最简单的图形操作,其算法网上可说是一搜一大把,确实如此,可就是这最简单的操作,网上的文章却五花八门,我拣几个试了一下,好像都不太理想,关键是算法太简单,实际操作效果不好,于是想,Photoshop的对比度还是较好的,而且也通用,但偏偏网上没有介绍它的算法,用了大半天时间研究了一下,再花了1个来小时写了个Delphi过程,试了一下,居然和Photoshop的对比度调整完全一样的效果!于是认真写了个测试程序,把亮度和对比度放在一起进行调整(亮度和对比度处理过程为各自独立的,其中亮度过程基本是本BLOG文章《GDI+ 在Delphi程序的应用 -- 调整图像亮度》的代码),可是效果却和Photoshop大不一样了,是什么原因呢,Photoshop的亮度调整算法是最简单的那种,与我的亮度过程做出来的是一样的(效果比较图参见《GDI+ 在Delphi程序的应用 -- 线性调整图像亮度》),而前面说了,对比度过程算法也是和Photoshop一样的,可放在一起调整就不行了,无论是先调整亮度,还是先调整对比度都这样。后来仔细分析了一下,Photoshop是用一个函数处理亮度/对比度,而且亮度调整是按对比度的正负分别对待的,下面是实现代码(包括一个简单的例子):

function _CheckRgb(Rgb: Integer): Integer;
asm
    test    eax, eax
    jge     @@1
    xor     eax, eax
    ret
@@1:
    cmp     eax, 255
    jle     @@2
    mov     eax, 255
@@2:
end;

procedure ImageBrightContrast(var Data: TImageData; Bright, Contrast, Threshold: Integer);
var
  vs: TGrayTable;
  cv: Single;
  i, v: Integer;
  height, dataOffset: Integer;
begin
  if (Bright = 0) and (Contrast = 0) then Exit;
  if Contrast <= -255 then cv := -1 else cv := Contrast / 255;
  if (Contrast > 0) and (Contrast < 255) then
    cv := 1 / (1 - cv) - 1;
  for i := 0 to 255 do
  begin
    if Contrast > 0 then v := _CheckRgb(i + bright) else v := i;
    if Contrast >= 255 then
    begin
      if v >= Threshold then v := 255 else v := 0;
    end
    else
      v := _CheckRgb(v + Round((v - Threshold) * cv));
    if Contrast <= 0 then vs[i] := _CheckRgb(v + bright) else vs[i] := v;
  end;
  asm
    mov     eax, Data
    call    _SetDataRegs
    mov     height, edx
    mov     dataOffset, ebx
    lea     esi, vs;
@@yLoop:
    push    ecx
@@xLoop:
    movzx   eax, [edi].TARGBQuad.Blue
    movzx   ebx, [edi].TARGBQuad.Green
    movzx   edx, [edi].TARGBQuad.Red
    mov     al, [esi+eax]
    mov     bl, [esi+ebx]
    mov     dl, [esi+edx]
    mov     [edi].TARGBQuad.Blue, al
    mov     [edi].TARGBQuad.Green, bl
    mov     [edi].TARGBQuad.Red, dl
    add     edi, 4
    loop    @@xLoop
    pop     ecx
    add     edi, dataOffset
    dec     height
    jnz     @@yLoop
@@Exit:
  end;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  bmp: TGpBitmap;
  g: TGpGraphics;
  data: TImageData;
begin
  bmp := TGpBitmap.Create('..\media\source1.jpg');
  g := TGpGraphics.Create(Canvas.Handle);
  g.DrawImage(bmp, 0, 0);
  data := LockGpBitmap(bmp);
  ImageBrightContrast(data, 50, 0, 121);
  UnlockGpBitmap(bmp, data);
  g.DrawImage(bmp, data.Width, 0);
  g.Free;
  bmp.Free;
end;

 

    下面对亮度/对比度的原理简单介绍一下。

    一、Photoshop对比度算法。可以用下面的公式来表示:

    (1)、nRGB = RGB + (RGB - Threshold) * Contrast / 255

    公式中,nRGB表示图像像素新的R、G、B分量,RGB表示图像像素R、G、B分量,Threshold为给定的阈值,Contrast为处理过的对比度增量。

    Photoshop对于对比度增量,是按给定值的正负分别处理的:

    当增量等于-255时,是图像对比度的下端极限,此时,图像RGB各分量都等于阈值,图像呈全灰色,灰度图上只有1条线,即阈值灰度;

    当增量大于-255且小于0时,直接用上面的公式计算图像像素各分量;

    当增量等于 255时,是图像对比度的上端极限,实际等于设置图像阈值,图像由最多八种颜色组成,灰度图上最多8条线,即红、黄、绿、青、蓝、紫及黑与白;

    当增量大于0且小于255时,则先按下面公式(2)处理增量,然后再按上面公式(1)计算对比度:

    (2)、nContrast = 255 * 255 / (255 - Contrast) - 255

    公式中的nContrast为处理后的对比度增量,Contrast为给定的对比度增量。

    二、图像亮度调整。本文采用的是最常用的非线性亮度调整(Phoposhop CS3以下版本也是这种亮度调整方式,CS3及以上版本也保留了该亮度调整方式的选项)。

    三、图像亮度/对比度综合调整算法。这个很简单,当亮度、对比度同时调整时,如果对比度大于0,现调整亮度,再调整对比度;当对比度小于0时,则相反,先调整对比度,再调整亮度。

    在亮度/对比度调整函数ImageBrightContrast中,首先按前面介绍的原理制造了一个256个元素大小的查找表,然后对图像数据逐像素按R、G、B分量值在查找表中取得调整后的数据,因此处理速度相当快。

    下面是对比度255时的运行截图:

 

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

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

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

 

你可能感兴趣的:(Delphi,Delphi图像处理)