网站公告列表

  没有公告

加入收藏
设为首页
联系站长
您现在的位置: 网络学院 >> 程序设计 >> Delphi编程 >> 文章正文
  GDI+ 在Delphi程序的应用 -- 仿Photoshop的明度调整            【字体:
GDI+ 在Delphi程序的应用 -- 仿Photoshop的明度调整
作者:佚名    文章来源:不详    点击数:    更新时间:2007-9-12    

        这几天研究了一下Photoshop的色相/饱和度命令,也就是所谓的HSB颜色模式,没完全搞明白,网上搜索也没一点结果,看了一些介绍HSB算法的文章,其实讲的就是HSV或者HSL的算法。
正在装载数据……

        关于PS色相/饱和度中的色相,就不用研究了,原理和HSV或者HSL的H都是一样的。

        而饱和度在-100,0,+100这三点上的效果与HSL完全一样,其它范围就有区别了,特别是在0 -- +100范围,调整时比HSL的H调整要平坦,所以有效调整幅度较大,有些图片调整到+50%以上还不觉很大失真(这里的“失真”是针对颜色中难看的斑点来说的,并不是说整个图片不觉失真),而HSL的H的正向调整10%以上就很难看了;与HSV则没一点是相同的,可见PS的色相/饱和度算法应该是在HSL基础上改进的。

        最令人困惑的是PS的明度调整,好像是“独立”于色相饱和度的。我们知道,要在程序中利用HSV或HSL模式调整V或者L,往往要先将RGB转换为HSV或HSL,或者至少要在其中将V或者L部分分离出来,修改后再转换为RGB模式(可参见我的文章《GDI+ 在Delphi程序的应用 -- 线性调整图像亮度 》分离HSL的L部分调整亮度),而PS的明度调整则不一样,完全不用转换RGB到所谓的HSB进行调整,直接写个函数就可以了,请看下面的Delphi过程及测试代码(分别用GDI+的TGpBitmap和Delphi的TBitmap测试),用于模仿PS明度调整(严格的说不叫模仿,而是实实在在的PS明度调整过程):


procedure PSBrightness(Data: TBitmapData; Value: Integer);
asm
    push    esi
    push    edi
    push    ebx

    mov     edi, eax
    mov     esi, [edi 
+ 16]
    mov     eax, [edi]
    shl     eax, 
1
    add     eax, [edi]
    mov     ebx, [edi 
+ 8]
    sub     ebx, eax
    mov     ecx, [edi 
+ 4]
  @yLoop:
    push    ecx
    mov     ecx, [edi]
  @xLoop:
    push    ecx
    mov     ecx, 
3
  @vLoop:
    push    ebx
    push    edx

    movzx   eax, [esi]
    test    edx, edx
    js      @@
3
    neg     eax
    add     eax, 
255
  @@
3:
    imul    eax, edx
    mov     ebx, 
255
    cdq
    idiv    ebx
    movzx   ebx, [esi]
    add     eax, ebx
    jns     @@
1
    xor     eax, eax
    jmp     @@
2
  @@
1:
    cmp     eax, 
255
    jle     @@
2
    mov     eax, 
255
  @@
2:
    mov     [esi], al
    inc     esi
    pop     edx
    pop     ebx
    loop    @vLoop
    pop     ecx
    loop    @xLoop
    add     esi, ebx
    pop     ecx
    loop    @yLoop

    pop     ebx
    pop     edi
    pop     esi
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  bmp1: TGpBitmap;
  data1: TBitmapData;
  g: TGpGraphics;
  r: TGpRect;
  value: Integer;
begin
  value := StrToInt(Edit1.Text) * 255 div 100;
  bmp1 := TGpBitmap.Create('001-1.jpg');
  r := GpRect(0, 0, bmp1.Width, bmp1.Height);
  g := TGpGraphics.Create(Handle, False);
  try
    g.DrawImage(bmp1, r);
    data1 := bmp1.LockBits(r, [imRead, imWrite], pf24bppRGB);
    try
      PSBrightness(data1, value);
    finally
      bmp1.UnlockBits(data1);
    end;
    g.TranslateTransform(195, 0);
    g.DrawImage(bmp1, r);

  finally
    g.Free;
    bmp1.Free;
  end;
end;

function GetImageData(Bmp: TBitmap): TBitmapData;
begin
  Bmp.PixelFormat := pf24bit;
  Result.Width := Bmp.Width;
  Result.Height := Bmp.Height;
  Result.Scan0 := Bmp.ScanLine[Result.Height - 1];
  Result.Stride := (((24 * Bmp.Width) + 31) and $ffffffe0) shr 3;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  bmp1: TBitmap;
  data1: TBitmapData;
  value: Integer;
begin
  bmp1 :
= TBitmap.Create;
  value :
= StrToInt(Edit1.Text) * 255 div 100;
  
try
    bmp1.LoadFromFile(
'001-1.bmp');
    Canvas.Draw(
00, bmp1);
    data1 :
= GetImageData(bmp1);
    PSBrightness(data1, value);
    Canvas.Draw(
1950, bmp1);
  
finally
    bmp1.Free;
  end;
end;

        可以看出,上面的PSBrightness过程没有依赖任何颜色模式转换,而是采用了下面这个伪代码公式:

if (value >= 0)
  RGB 
= RGB + (255 - RGB) * value / 255;
else
  RGB 
= RGB + RGB * value / 255;

其中RGB分别表示颜色的R、G、B,value为明度值。那么这个公式的含义是什么的,其实就是HSL转换为RGB的L部分的公式变形,我在《GDI+ 在Delphi程序的应用 -- 线性调整图像亮度 》中采用的公式和它形式是一样的,只是计算基数不同 :

= L - 128;
if (L >= 0)
  RGB 
= RGB + (255 - RGB) * L / 128;
else
  RGB 
= RGB + RGB * L / 128;

        前者使用的是value的全范围255或者100%(公式后的/255改为/100),而后者采用的是L的1/2,也就是128或者50%(就这点区别,效果可就大相径庭了),而且要利用L调整亮度,必须从HSL空间中获取L再加上调整值,而PS明度调整则不需要从HSB的得到原来的B,这就意味着B在HSB中始终为“0”!虽然PS的明度调整是和色相/饱和度调整放在一起的,但完全不依赖于色相/饱和度,这就是我感觉其好像是“独立”的原因,也是研究PS饱和度算法不果的重要原因(一个B=0的HSB模式,光靠HS部分怎样正确转换为R、G、B?要知道HSV的V和HSL中的L在正确转换为RGB模式中至关重要!)。

        假如我的感觉是正确的,那么PS“独立”的明度调整又是什么原理呢?我们知道,一般的非线性RGB亮度调整只是在原有R、G、B值基础上增加和减少一定量来实现的,而PS的明度调整原理还得从前面那个公式上去找。我们将正向明度调整公式:RGB = RGB + (255 - RGB)  * value / 255转换为RGB = (RGB * (255 - value) + 255 * value) / 255,如果value用1表示最大值255,则为RGB = RGB * (255 - value) + 255 * value,可以看出什么呢?凡是知道图像合成的人都知道这个公式,其实PS的明度调整是采用Alpha合成方式,这里的value就是Alpha,公式前面部分RGB * (255 - value)的是图像部分,后面的255 * value部分则是一个白色遮照层,明度越大,遮照层的Alpha越大,图像就越谈,反之亦然。而明度的负调整则是以一个黑色遮照层来完成的。负100%就全黑了。只有遮照层Alpha=0,也就是明度值为0时,才是完完全全的图片显示。要验证上面的说法很简单,一是运行我的测试代码,而是在PS中,用一个全白或全黑图层覆盖在一张图片上,调整这个层的不透明度,可以看出和明度调整效果完全一样!

        其实,我只是对PS的饱和度调整感兴趣,原因前面已经说了,比HSV和HSL的饱和度调整效果要好,范围要大,饱和度算法没研究出来,到搞了个明度调整过程。希望知道PS饱和度算法的朋友不吝赐教,本人不甚感激!

  如有错误请来信指正:maozefa@hotmail.com




本文来源:http://blog.csdn.net/maozefa/archive/2007/09/04/1772418.aspx
站内文章搜索 高级搜索
文章录入:admin    责任编辑:admin 
  • 上一篇文章:

  • 下一篇文章:
  • 发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
    最新热点 最新推荐 相关文章
     在delphi中使用xml文档有…
     初探delphi 7 中的插件编…
     delphi 2006(dexter) & …
     获得windows的版本信息。
     “序列号输入助手”源代…
     rs232串口通讯模块
     ado方式下判断数据表是否…
  • Java的网络编程(TCP/IP)

  • Ant入门-配置和使用     选…

  • 浅析Spring框架下PropertyPl…

  • jsp重定向forward和sendRedi…

  • MVP——Model-Viewer-Presen…

  • C++ Object Model

  • Solaris10下,使用SunStudio…

  • 内存管理内幕--Jonathan Bar…

  • constructor and destructor

  • delegate C#关键字 (委托类型…

  •   网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)
    网络学院©2007 www.23book.net
    为您提供web编程,vb编程,vc编程,服务器架设管理,数据库设计等方面的知识 站长:David