【PS算法理论探讨二】 Photoshop中图层样式之 投影样式 算法原理初探讨。
接下来几篇文章我们将稍微简单的探索下PS中多种图层混合模式的算法内部原理,因为毕竟没有这方面的官方资料,所以很多方面也只是本人自己的探索和实践,有可能和实际的情况有着较大的差异。
在PS的实践中,图层样式的存在使得一个简单的图形蜕变为一个丰富的样式成为可能,而在PS的各个版本中,图层样式的选项也越来越丰富, 功能也越来越强大。作为一个成功的图形和图形编辑软件,图层样式功能是否缺失也可以看成其是否具有强大生命力的一个典型标志,比如作为图像开源界的扛把子 GIMP 就没有这个功能。而平时我们能看到的商业软件也鲜有这个功能。
在我使用的CS6版本的PS中,提供了斜面和浮雕、描边、内阴影、内发光、光泽、颜色叠加、渐变叠加、图案叠加、外发光、投影等10中图层样式,在我后续的文章中将分别讲述除了 外发光和内发光 之外的其他8种样式的原理和实现。
本文将简单讲述投影样式的原理,投影样式的可控参数界面如下所示:
参数包含了混合模式、不透明度、角度、距离、扩展、大小‘等高线、消除锯齿、杂色等。我们先从大的方向开始讲起。
在PS中,如果我们打开一幅JPG图像(一般为RGB格式的),我们会发现PS为该图像所其的名字为背景层,而且层右侧有一个锁的符号,如下所示:
如果此时我们双击这个层,出现的是新建图层的界面,而不是图层样式的旋向,如上图所示。
但是,如果我们打开的是一副带透明通道的32位PNG图像,此时系统默认就是用图层0为该图像命名,而且后侧没有锁的符号。
此时双击图层符号,则打开了图层样式对话框。
通过这个现象可以做个简单的猜测,图层样式需要Alpha通道,而实际的研究也表明,大部分的图层样式(除颜色叠加、渐变叠加、图案叠加,我局的应该把他们从样式中开除)都是对Alpha通道的数据进行一定处理后,再配合某种颜色和原图进行一定程度的融合。
完美甚至可以沿用另外一种流行的说法,图层样式其内在实际上是按照一定的规则虚拟了1个或几个图层,然后通过不同的图层位置(位于上部或下部)、混合样式、不透明度等和原图进行混合。这个也是所有的样式里的混合模式、不透明度的概念源头所在。
再次回到这个投影样式吧。 在PS里随意的弄下这个效果,可以直观的感觉到这个样式的作用是根据所选参数在当前层下部虚拟一个阴影层。
那么我的实现思路核心如下:
第一步: 按照指定的角度将原图的Alpha信息偏移一定的角度,偏移后无效的区域Alpha设置为0。
原始图像 原始图像的Alpha通道信息 按照指定的角度偏离后的Alpha信息(角度30, 距离20)
简单的代码如下所示:
float SinV = -sinf(Angle / 180.0 * 3.1415926f);
float CosV = cosf(Angle / 180.0 * 3.1415926f);
int Left = (int)(Distance * CosV + 0.499999f);
int Top = (int)(Distance * SinV + 0.499999f);
// 计算Alpha通道的偏移信息
for (int Y = 0; Y < Height; Y++)
{
int NewY = Y + Top;
if ((NewY < 0) || (NewY >= Height))
{
memset(ShiftA + Y * Width, 0, Width);
}
else
{
unsigned char *LinePD = ShiftA + Y * Width;
for (int X = 0; X < Width; X++)
{
int NewX = X + Left;
if ((NewX < 0) || (NewX >= Width))
{
LinePD[X] = 0;
}
else
{
int Index = NewY * Stride + NewX * 4 + 3;
LinePD[X] = Src[Index];
}
}
}
}
界面中的角度和距离共同决定了这个Alpha通道偏离的程度。
对面后面的大小和扩展参数,我们结合网络中的一些参考资料,通过本人的实践,基本上可以确定是使用的如下算法。
首先我们把大小设置为10,然后把扩展设置为100%,对于上面的图,可达到如下效果:
大小为10,扩展为100%时的结果 大小为0时的结果
可以看到,当大小为10,扩展100%时,阴影部分变的更为粗大,通过测试,我们发现这个实际上应该是对前述偏移后的Alpha选区进行了一定程度的圆形最大值算法,我们是是圆形,我们可以比较下同样半径的圆形和矩形最大值的结果区别:
半径为10的矩形最大值 半径为10的圆形最大值
很明显的可以看到,矩形最值不能保留原来光滑的圆角,而圆形可以。
因此,我们推测扩展就是对选区进行圆形的最大值算法,而最大值的半径和大小以及扩展的数据有关,根据PS界面扩展后面的% 百分比可以认定他为大小的 百分比。
而大小参数,明显可以看到,随着大小的变大,阴影越来越模糊,因此,可以猜测这个为对Alpha进行模糊。不过我测试所,似乎并不是高斯模糊,不晓得实际为何种模糊。
// 第二步对这个Alpha进行下堵窒,算法上就是圆形的最大值算法
int ChokeSize = (Size * Choke + 49) / 100;
if (ChokeSize != 0) // 堵窒
{
Status = IM_MaxFilter_Round_Gray(ShiftA, ShiftA, Width, Height, Width, ChokeSize);
if (Status != IM_STATUS_OK) goto FreeMemory;
}
// 第三步对Alpha进行羽化了,高斯模糊(但是PS的不晓得属于那种模糊)
if ((Size != 0) && (Size != ChokeSize))
{
Status = IM_GaussBlur(ShiftA, ShiftA, Width, Height, Width, Size - ChokeSize);
if (Status != IM_STATUS_OK) goto FreeMemory;
}
那么下面还有一个关键的东西,就是那个等高线,这个东西网络上把他说的好神奇,各路大神都有发表感言。我看啊,都是假神,那个东西其实就是如他表面所表现出来的东西,就是一个曲线调整,而且和PS本身的曲线也是一个意思,只不过他调整的不是图像里的RGB,而是这里的Alpha,通过动态调整这个Alpha获得不同的结果。
// 第四步对选区进行等高线算法,实际上就是一个查表
for (int Y = 0; Y < Height * Width; Y++)
{
ShiftA[Y] = Table[ShiftA[Y]];
}
那么最后一步,就是根据不透明度、混合模式以及用户提供的背景色来创建一个新的图层,这个图层位于当前层下方,进行图层混合了。如果是一个单独的图层,由于这个图层下面没有其他图层,混合样式在这里其实是起不到作用的(除了那个另类的溶解),这个时候一个简单的混合代码如下所示:
for (int Y = 0; Y < Height; Y++)
{
unsigned char *LinePD = Dest + Y * Stride;
unsigned char *LinePS = Src + Y * Stride;
unsigned char *LinePA = ShiftA + Y * Width;
for (int X = 0; X < Width; X++)
{
int B1 = BackColor_B, G1 = BackColor_G, R1 = BackColor_R, A1 = LinePA[X];
int B2 = LinePS[0], G2 = LinePS[1], R2 = LinePS[2], A2 = LinePS[3];
int NewA1 = A1 * Opacity;
int BlendAlpha = IM_Div255(A2 * NewA1);
int Alpha = A2 * 255 + NewA1 - BlendAlpha;
if (Alpha != 0)
{
LinePD[0] = (B1 * NewA1 + B2 * A2 * 255 - BlendAlpha * B1) / Alpha;
LinePD[1] = (G1 * NewA1 + G2 * A2 * 255 - BlendAlpha * G1) / Alpha;
LinePD[2] = (R1 * NewA1 + R2 * A2 * 255 - BlendAlpha * R1) / Alpha;
}
else
{
LinePD[0] = LinePS[0];
LinePD[1] = LinePS[1];
LinePD[2] = LinePS[2];
}
LinePD[3] = IM_Div255(Alpha);
LinePS += 4;
LinePD += 4;
}
}
注意这里的混合的Alpha需要改变。
至于界面里的消除锯齿应该是针对曲线的,这个就是在曲线插值时加上抗锯齿功能,那个什么杂色之类的无所谓,就是在Alpha信息里加上一些随机噪音。没啥好难的。
当然,经过一些其他测试,发现PS里的投影还有一些更为复杂的逻辑,和本文的讲述不一致,但是本文的效果也在一定程度上能局部复原结果,对于一些普通的应用是足以完成任务了
提供一个链接工大家测试:https://files.cnblogs.com/files/Imageshop/DropShadow.rar
如果想时刻关注本人的最新文章,也可关注公众号:
【PS算法理论探讨二】 Photoshop中图层样式之 投影样式 算法原理初探讨。的更多相关文章
- 装载:对CSS中的Position、Float属性的一些深入探讨
对CSS中的Position.Float属性的一些深入探讨 对CSS中的Position.Float属性的一些深入探讨 对于Position.Float我们在平时使用上可以说是使用频率非常高的两个 ...
- 【转】shell命令中>/dev/null 2>&1的实现原理
异步执行 exec("/alidata/server/php/bin/php /nas/wxdoctor/index.php App/Common/WordsPic/user_id/&quo ...
- (二十)WebGIS中图层树功能的设计和实现
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 在GIS的桌面工具中,比如arcgis desktop或者S ...
- (十二) WebGIS中矢量图层的设计
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.前言 在前几章中我们已经了解了什么是矢量查询.屏幕坐标与地理坐标之 ...
- ps中图层混合模式、多图层叠加、不透明度、填充、图层样式详解
图像领域中,通过进行一下想法的时候,都要通过用ps看下是不是合理,而ps中图层是必用的一个功能,下面详解一下图层有关的叠加原理. 基本顺序是图层从下往上继续, 先计算图层的填充,再计算样式.最后计算不 ...
- SLAM中的优化理论(二)- 非线性最小二乘
本篇博客为系列博客第二篇,主要介绍非线性最小二乘相关内容,线性最小二乘介绍请参见SLAM中的优化理论(一)-- 线性最小二乘.本篇博客期望通过下降法和信任区域法引出高斯牛顿和LM两种常用的非线性优化方 ...
- [Java 泥水匠] Java Components 之二:算法篇之项目实践中的位运算符(有你不懂的哦)
作者:泥沙砖瓦浆木匠网站:http://blog.csdn.net/jeffli1993个人签名:打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节. 交流QQ群:[编程之美 365234583] ...
- 【C#代码实战】群蚁算法理论与实践全攻略——旅行商等路径优化问题的新方法
若干年前读研的时候,学院有一个教授,专门做群蚁算法的,很厉害,偶尔了解了一点点.感觉也是生物智能的一个体现,和遗传算法.神经网络有异曲同工之妙.只不过当时没有实际需求学习,所以没去研究.最近有一个这样 ...
- [转载] 散列表(Hash Table)从理论到实用(中)
转载自:白话算法(6) 散列表(Hash Table)从理论到实用(中) 不用链接法,还有别的方法能处理碰撞吗?扪心自问,我不敢问这个问题.链接法如此的自然.直接,以至于我不敢相信还有别的(甚至是更好 ...
随机推荐
- Hive处理Json数据
Json 格式的数据处理 Json 数据格式是我们比较常用的的一种数据格式,例如埋点数据.业务端的数据.前后端调用都采用的是这种数据格式,所以我们很有必要学习一下这种数据格式的处理方法 准备数据 ca ...
- java 适配器模式实现代码
目录 1.适配器模式 1.1.类适配器 1.2.对象适配器 2.适配器模式实例 1.适配器模式 适配器模式可以分为类适配器和对象适配器. 1.1.类适配器 //目标接口 interface Targe ...
- 洛谷 P5400 - [CTS2019]随机立方体(组合数学+二项式反演)
洛谷题面传送门 二项式反演好题. 首先看到"恰好 \(k\) 个极大值点",我们可以套路地想到二项式反演,具体来说我们记 \(f_i\) 为钦定 \(i\) 个点为极大值点的方案数 ...
- excel-合并多个Excel文件--VBA合并当前目录下所有Excel工作簿中的所有工作表
在网上找EXCEL多文件合并的方法,思路: 一.Linux 或者window+cmder,直接用命令行cat合并EXCEL文件,但是,需要安装辅助东西才能直接处理(也许也不可以,但是,可以用文件格式转 ...
- do{...}while(0)的用法
零.导引第一次见到 do{...}while(0)是在学习libevent的时候,看到里面有很多类似#define TT_URI(want) do { \ char *ret = evhttp_uri ...
- 网站性能调优实战-学相伴KuangStudy
面对并发我们是如何优化KuangStudy网站性能的? 每个项目都会随着用户和数据的增长调整架构,来面对未来的问题,我们也不例外,在1月5号我们平台正式公测后,引起了很多观众的热烈反响,仅仅4天,注册 ...
- NuxtJS的AsyncData和Fetch使用详解
asyncData 简介 asyncData 可以用来在客户端加载 Data 数据之前对其做一些处理,也可以在此发起异步请求,提前设置数据,这样在客户端加载页面的时候,就会直接加载提前渲染好并带有数据 ...
- docker安装jumpserver
注意MySQL的密码设置要有复杂度,否则jumpserver用不了 #先准备一台服务器安装MySQL和redis(注意官网版本要求) root@ubuntu:~# docker pull mysql: ...
- Gitlab安装操作说明书
一.Gitlab安装操作步骤 登录官方网站https://about.gitlab.com/downloads/根据你所需要的系统版本,作者使用的是centos6, 检查您的服务器是否符合硬件要求.g ...
- mybatis的dao层和service层的编码设计的配置
/** 书写pojo类------>dao接口------>resources下建立同路径的dao.xml------>配置applicationContext.xml文件 **/ ...