这几天研究了一下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明度调整过程):
说明:为了统一《GDI+ 在Delphi程序的应用》系列文章所用数据类型和图像处理格式,本文代码已作了修订,代码中所用Gdiplus单元下载地址及BUG更正见文章《GDI+ for VCL基础 -- GDI+ 与 VCL》。(2008.8.18记)
数据类型:
-
type
-
-
TImageData=packedrecord
-
Width:LongWord;
-
Height:LongWord;
-
Stride:LongWord;
-
PixelFormat:LongWord;
-
Scan0:Pointer;
-
Reserved:LongWord;
-
end;
-
PImageData=^TImageData;
-
-
-
functionGetImageData(Bmp:TBitmap):TImageData;
-
begin
-
Bmp.PixelFormat:=pf32bit;
-
Result.Width:=Bmp.Width;
-
Result.Height:=Bmp.Height;
-
Result.Scan0:=Bmp.ScanLine[Bmp.Height-1];
-
Result.Stride:=Result.Widthshl2;
-
-
end;
过程代码:
-
-
procedurePSBrightness(Data:TImageData;Value:Integer);
-
asm
- pushebp
- pushesi
- pushedi
- pushebx
-
movedi,[eax+16]
-
movebp,[eax+4]
- imulebp,[eax]
-
movesi,edx
-
movebx,255
- cld
-
@PixelLoop:
-
movecx,3
-
@vLoop:
-
movzxeax,[edi]
- pusheax
- testesi,esi
-
js@@1
-
negeax
-
addeax,ebx
-
@@1:
-
imuleax,esi
-
cdq
- idivebx
- popedx
- addeax,edx
-
jns@@2
-
xoreax,eax
-
jmp@@3
-
@@2:
- cmpeax,ebx
-
jle@@3
- moveax,ebx
-
@@3:
-
stosb
-
loop@vLoop
-
incedi
- decebp
-
jnz@PixelLoop
- popebx
- popedi
- popesi
- popebp
-
end;
-
-
procedureGdipPSBrightness(Bmp:TGpBitmap;Value:Integer);
-
var
- Data:TBitmapData;
-
begin
-
ifValue=0thenExit;
-
Data:=Bmp.LockBits(GpRect(0,0,Bmp.Width,Bmp.Height),[imRead,imWrite],pf32bppARGB);
-
try
- PSBrightness(TImageData(Data),Value);
-
finally
-
Bmp.UnlockBits(Data);
-
end;
-
end;
-
-
procedureBitmapPSBrightness(Bmp:TBitmap;Value:Integer);
-
begin
-
ifValue<>0then
- HSLBrightness(GetImageData(Bmp),Value);
-
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=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 * (1 - value) + 255 * value,可以看出什么呢?凡是知道图像合成的人都知道这个公式,其实PS的明度调整是采用Alpha合成方式,这里的value就是Alpha,公式前面部分RGB * (1 - value)的是图像部分,后面的255 * value部分则是一个白色遮照层,明度越大,遮照层的Alpha越大,图像就越谈,反之亦然。而明度的负调整则是以一个黑色遮照层来完成的。负100%就全黑了。只有遮照层Alpha=0,也就是明度值为0时,才是完完全全的图片显示。要验证上面的说法很简单,一是运行我的测试代码,而是在PS中,用一个全白或全黑图层覆盖在一张图片上,调整这个层的不透明度,可以看出和明度调整效果完全一样!
其实,我只是对PS的饱和度调整感兴趣,原因前面已经说了,比HSV和HSL的饱和度调整效果要好,范围要大,饱和度算法没研究出来,到搞了个明度调整过程。希望知道PS饱和度算法的朋友不吝赐教,本人不甚感激!
测试代码:
-
procedureTForm1.Button1Click(Sender:TObject);
-
var
- Image:TGpBitmap;
- g:TGpGraphics;
-
begin
-
Image:=TGpBitmap.Create('D:/VclLib/GdiplusDemo/Media/20041001.jpg');
-
g:=TGpGraphics.Create(Handle,False);
-
g.DrawImage(Image,10,10);
-
GdipPSBrightness(Image,30);
-
g.DrawImage(Image,10,220);
-
Image.Free;
-
g.Free;
-
end;
-
procedureTForm1.Button2Click(Sender:TObject);
-
var
- Image:TBitmap;
-
begin
-
Image:=TBitmap.Create;
-
Image.LoadFromFile('D:/VclLib/GdiplusDemo/Media/20041001.bmp');
-
Canvas.Draw(10,10,Image);
-
BitmapPSBrightness(Image,-30);
-
Canvas.Draw(10,220,Image);
-
Image.Free;
-
end;
如有错误请来信指正:maozefa@hotmail.com
分享到:
相关推荐
GDI+入门指导书------经典 非常适合于GDI+初学者
——在GDI+ Painter应用程序中添加颜色、钢笔和画笔 总结 第5章 颜色、字体和文本 5.1 访问Graphics对象 5.2 使用颜色 5.3 使用字体 5.4 使用文本和字符串 5.5 渲染文本的质量和性能 5.6 高级版式 5.7 一个...
DELPHI版的GDI++库,内有Demos.
windwos C++ gdi++实现jpg图像压缩-图像裁剪和缩放-图像格式转换-图像dpi修改
WINDOWS GDI和GDI+编程实例剖析. - READ
使用GDI+进行图形缩放、拖动,多种图片格式支持,仅简单示例。 问题源贴:http://bbs.csdn.net/topics/390638094
——在GDI+ Painter应用程序中添加颜色、钢笔和画笔 总结 第5章 颜色、字体和文本 5.1 访问Graphics对象 5.2 使用颜色 5.3 使用字体 5.4 使用文本和字符串 5.5 渲染文本的质量和性能 5.6 高级版式 5.7 一个...
GDI+程序设计 GDI+程序设计 GDI+程序设计 GDI+程序设计
GDI+程序设计.pdf,书籍和随书源码。
在Delphi中使用GDI+,范例中包含GDI+各种效果的测试。
其中,ULONG_PTR是一个DWORD数据类型,该成员变量用来保存GDI+被初始化后在应用程序中的GDI+标识,以便能在应用程序退出后,引用该标识来调用Gdiplus:: GdiplusShutdown来关闭GDI+。 (2)在应用类中添加...
一本为C#开发人员准备的图形图像处理技术的书籍
使用Delphi+GDI实现图片的镜像翻转,有需要的可以试试。
Delphi GDI+
一本不可多得的GDI+绘图书籍,网上GDI+介绍其实挺少的,研究GDI+绘图时,浪费了不少时间,找了很久,发现了一本好书,在万分激动的心情下,将此书分享给大家,希望在GDI+绘图上给您一些指导。
GDI+对话框的显示,里面有5个特效和打开文件的代码
C#GDI+小游戏 飞机大战C#GDI+小游戏 飞机大战C#GDI+小游戏 飞机大战C#GDI+小游戏 飞机大战C#GDI+小游戏 飞机大战
使用C#编写的贪吃蛇游戏,游戏相关说明可以参考我的博文在《WinForm中使用GDI+编写游戏--贪吃蛇》