C语言数字图像处理进阶---1.1 Photoshop图层算法
Posted Trent1985
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言数字图像处理进阶---1.1 Photoshop图层算法相关的知识,希望对你有一定的参考价值。
前言
本专栏为《零基础C语言数字图像处理利器》的进阶版,对于没有任何C语言基础的同学们,在学习了《零基础C语言数字图像处理利器》之后,已经对C语言进行图像处理有了初步的认识和入门,如果想要进一步向图像算法工程师迈进,那么,本专栏将从图像滤镜特效方面帮助大家,真正的做一些可以工程应用的滤镜特效算法,这类图像滤镜特效算法广泛应用于各大PC/移动端修图软件,如美图秀秀、天天P图、抖音快手等等。在学习了本专栏之后,你将可以轻松从事滤镜特效算法开发工作,甚至可以自己开发滤镜特效类软件、app等。
本专栏继续以C语言为唯一工具,来实现相关的图像算法,不依赖任何第三方算法库,真正的帮助大家理解算法、学习算法、实现算法、应用算法!
千里之行始于足下,作为第一讲,我们将介绍Photoshop中最为常用的图层混合算法,并用C语言来实现,让我们愉快的开始吧!
图层混合算法
图层混合算法来自Photoshop,一共有27种图层混合模式,包括:
正常模式(Normal)、溶解模式(Dissolve)、变暗模式(Darken)、正片叠底模式(Multiply)、颜色加深模式(Color Burn)、线性加深模式(Linear Burn)、深色模式(Dark)、变亮模式(Lighten)、滤色模式(Screen)、颜色减淡模式(Color Dodge)、线性减淡模式(Linear Dodge)、浅色模式(Lighter Color)、叠加模式(Overlay)、柔光模式(Soft Light)、强光模式(Hard Light)、亮光模式(Vivid Light)、线性光模式(Linear Light)、点光模式(Pin Light)、实色混合(Hard Mix)、差值模式(Difference)、排除模式(Exclusion)、减去模式(Subtract)、划分模式(Divide)、色相模式(Hue)、饱和度模式(Saturation)、颜色模式(Color)、亮度模式(Luminosity);
其中最后四种属于颜色空间变换。
在Photoshop中,图层混合的概念是针对两个图层而言,一个是基色图层,另一个是混合色图层,基色图层在混合色图层的下面,对于混合色图层执行相应的图层混合算法,会得到相应的效果 呈现。如下图所示:
这里我们假设基色图层为Base,混合色图层为Mix,图层混合的结果图层为Res,实际上图层就是图像,每一个图层就是一张图像,这样讲对于初学者就很容易理解。在PS中还有一个透明度和填充的参数调节,这里我们定义透明度为alpha,填充为100(该参数可以忽略,实际上也是一种alpha混合),我们将实现带alpha调节的图层混合算法。
在实现之前,我们将定义一个大概的接口,形式如下:
inline void BlendXX(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
输入为基色图层Base和混合色图层Mix的RGB值,alpha透明度,输出为结果图层Res的RGB值,定义 为指针类型;
下面开始图层混合模式的讲解。
正常模式
正常模式实际上就是一个简单的alpha融合,公式如下:
其中,alpha是一个0-100的透明度调整,这里是对应Photoshop中的数值范围,实际上我们也可以定义为0-255;
代码如下:
//正常模式
inline void BlendNormal(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = (baseR * (100 - alpha) + mixR * alpha) / 100;
*resG = (baseG * (100 - alpha) + mixG * alpha) / 100;
*resB = (baseB * (100 - alpha) + mixB * alpha) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
溶解模式
溶解模式比较特殊,我们知道,从字面意思来看,对于两个物体相互溶解,那么这个过程是在接触面发生的的小颗粒的相互渗透过程,这个接触面就可以理解为基色图层和混合色图层之间,相互渗透就表现为随机颗粒的呈现,在PS中如果alpha从0到100变化时,可以发现,从基色图层上不断出现一些混合图层效果的随机小颗粒,直到完全变化为混合图层效果。
这个过程如何实现,没有固定的公式,这里本人给出一种实现算法。
我们设定一个阈值threshold,当阈值大于alpha时显示混合图层Mix,反之,显示基色图层Base,但是单单这样仅仅是个正常的图层混合模式,这里,要出现随机颗粒的混合效果,我们可以将阈值随机化,让它在0-100之间随机,这样也就相当于随机alpha融合。
代码如下:
//均匀随机数
inline double AverageRandom(double a, double b)
{
double res = 0;
do
{
res = rand() * (1.0 / RAND_MAX);
} while (res <= a || res > b);
return res;
};
//溶解模式
inline void BlendDissolve(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
int threshold = (int)(AverageRandom(0, 1.0) * 100.0);
if (threshold > alpha)
{
*resR = mixR;
*resG = mixG;
*resB = mixB;
}
else
{
*resR = baseR;
*resG = baseG;
*resB = baseB;
}
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
变暗模式
变暗模式的效果表现就是取基色图层和混合色图层中颜色较暗的作为结果图层颜色效果,公式如下:
代码如下:
//变暗模式
inline void BlendDarken(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = (baseR > mixR ? mixR : baseR);
*resG = (baseG > mixG ? mixG : baseG);
*resB = (baseB > mixB ? mixB : baseB);
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
对比效果如下:
本文效果(alpha=50) PS效果(alpha = 50)
正片叠底模式
公式如下:
代码如下:
//正片叠底模式
inline void BlendMultiply(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = baseR * mixR / 255;
*resG = baseG * mixG / 255;
*resB = baseB * mixB / 255;
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
颜色加深模式
公式如下:
注意,这里Mix作为分母,为了避免为0,代码实现是(Mix+1)处理,实际上PS里也是这么做的,下文其他图层混合分母处理同理。
代码如下:
//颜色加深模式
inline void BlendColorBurn(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = CLIP3((baseR + mixR - 255) * 255 / (mixR + 1), 0, 255);
*resG = CLIP3((baseG + mixG - 255) * 255 / (mixG + 1), 0, 255);
*resB = CLIP3((baseB + mixB - 255) * 255 / (mixB + 1), 0, 255);
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
对比效果如下:
本文效果(alpha=50) PS效果(alpha = 50)
线性加深模式
公式如下:
代码如下:
//线性加深模式
inline void BlendLinearBurn(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = CLIP3(baseR + mixR - 255, 0, 255);
*resG = CLIP3(baseG + mixG - 255, 0, 255);
*resB = CLIP3(baseB + mixB - 255, 0, 255);
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
对比效果如下:
本文效果(alpha=50) PS效果(alpha = 50)
深色模式
公式如下:
代码如下:
//深色模式
inline void BlendDark(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
int baseSum = 0, mixSum = 0;
baseSum = baseR + baseG + baseB;
mixSum = mixR + mixG + mixB;
if (baseSum<mixSum)
{
*resR = baseR;
*resG = baseG;
*resB = baseB;
}
else
{
*resR = mixR;
*resG = mixG;
*resB = mixB;
}
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
变亮模式
变亮模式顾名思义就是选择较亮的作为结果图层颜色,公式如下:
代码如下:
//变亮模式
inline void BlendLighten(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = (baseR>mixR ? baseR : mixR);
*resG = (baseG>mixG ? baseG : mixG);
*resB = (baseB>mixB ? baseB : mixB);
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
对比效果如下:
本文效果(alpha=50) PS效果(alpha = 50)
滤色模式
公式如下:
代码如下:
//滤色模式
inline void BlendScreen(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = 255 - (255 - mixR) * (255 - baseR) / 255;
*resG = 255 - (255 - mixG) * (255 - baseG) / 255;
*resB = 255 - (255 - mixB) * (255 - baseB) / 255;
*resR = CLIP3((*resR * alpha + baseR * (100 - alpha)) / 100, 0, 255);
*resG = CLIP3((*resG * alpha + baseG * (100 - alpha)) / 100, 0, 255);
*resB = CLIP3((*resB * alpha + baseB * (100 - alpha)) / 100, 0, 255);
};
对比效果如下:
本文效果(alpha=50) PS效果(alpha = 50)
颜色减淡模式
公式如下:
代码如下:
//颜色减淡模式
inline void BlendColorDodge(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = CLIP3(baseR + (baseR * mixR) / (256 - mixR), 0, 255);
*resG = CLIP3(baseG + (baseG * mixG) / (256 - mixG), 0, 255);
*resB = CLIP3(baseB + (baseB * mixB) / (256 - mixB), 0, 255);
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
对比效果如下:
本文效果(alpha=50) PS效果(alpha = 50)
线性减淡模式
公式如下:
代码如下:
//线性减淡模式
inline void BlendLinearDodge(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = CLIP3(baseR + mixR, 0, 255);
*resG = CLIP3(baseG + mixG, 0, 255);
*resB = CLIP3(baseB + mixB, 0, 255);
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
对比效果如下:
本文效果(alpha=50) PS效果(alpha = 50)
浅色模式
公式如下:
代码如下:
//浅色模式
inline void BlendLighterColor(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
int baseSum = 0, mixSum = 0;
baseSum = baseR + baseG + baseB;
mixSum = mixR + mixG + mixB;
if (baseSum>mixSum)
{
*resR = baseR;
*resG = baseG;
*resB = baseB;
}
else
{
*resR = mixR;
*resG = mixG;
*resB = mixB;
}
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
叠加模式
公式如下:
代码如下:
//叠加模式
inline void BlendOverlay(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = CLIP3(((baseR <= 128) ? (mixR * baseR / 128) : (255 - (255 - mixR) * (255 - baseR) / 128)), 0, 255);
*resG = CLIP3(((baseG <= 128) ? (mixG * baseG / 128) : (255 - (255 - mixG) * (255 - baseG) / 128)), 0, 255);
*resB = CLIP3(((baseB <= 128) ? (mixB * baseB / 128) : (255 - (255 - mixB) * (255 - baseB) / 128)), 0, 255);
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
柔光模式
公式如下:
代码如下:
//柔光模式
inline void BlendSoftLight(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = CLIP3(mixR > 128 ? ((int)((float)baseR + ((float)mixR + (float)mixR - 255.0f)*((sqrt((float)baseR / 255.0f))*255.0f - (float)baseR) / 255.0f)) :
((int)((float)baseR + ((float)mixR + (float)mixR - 255.0f)*((float)baseR - (float)baseR*(float)baseR / 255.0f) / 255.0f)),0,255);
*resG = CLIP3(mixG > 128 ? ((int)((float)baseG + ((float)mixG + (float)mixG - 255.0f)*((sqrt((float)baseG / 255.0f))*255.0f - (float)baseG) / 255.0f)) :
((int)((float)baseG + ((float)mixG + (float)mixG - 255.0f)*((float)baseG - (float)baseG*(float)baseG / 255.0f) / 255.0f)), 0, 255);
*resB = CLIP3(mixB > 128 ? ((int)((float)baseB + ((float)mixB + (float)mixB - 255.0f)*((sqrt((float)baseB / 255.0f))*255.0f - (float)baseB) / 255.0f)) :
((int)((float)baseB + ((float)mixB + (float)mixB - 255.0f)*((float)baseB - (float)baseB*(float)baseB / 255.0f) / 255.0f)), 0, 255);
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
强光模式
公式如下:
代码如下:
//强光模式
inline void BlendHardLight(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = CLIP3(mixR <= 128 ? (mixR * baseR / 128) : (255 - (255 - mixR) * (255 - baseR) / 128), 0, 255);
*resG = CLIP3(mixG <= 128 ? (mixG * baseG / 128) : (255 - (255 - mixG) * (255 - baseG) / 128), 0, 255);
*resB = CLIP3(mixB <= 128 ? (mixB * baseB / 128) : (255 - (255 - mixB) * (255 - baseB) / 128), 0, 255);
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
亮光模式
公式如下:
代码如下:
//亮光模式
inline void BlendVividLight(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
if (mixR <= 128)
*resR = (baseR - (255 - baseR)*(255 - 2 * mixR) / (2 * mixR + 1));
else
*resR = baseR + (baseR*(2 * mixR - 255)) / (255 - (2 * mixR - 255) + 1);
if (mixG <= 128)
*resG = (baseG - (255 - baseG)*(255 - 2 * mixG) / (2 * mixG + 1));
else
*resG = baseG + (baseG*(2 * mixG - 255)) / (255 - (2 * mixG - 255) + 1);
if (mixB <= 128)
*resB = (baseB - (255 - baseB)*(255 - 2 * mixB) / (2 * mixB + 1));
else
*resB = baseB + (baseB*(2 * mixB - 255)) / (255 - (2 * mixB - 255) + 1);
*resR = CLIP3(*resR, 0, 255);
*resG = CLIP3(*resG, 0, 255);
*resB = CLIP3(*resB, 0, 255);
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
线性光模式
公式如下:
代码如下:
//线性光模式
inline void BlendLinearLight (int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = CLIP3(2 * mixR + baseR - 255, 0, 255);
*resG = CLIP3(2 * mixG + baseG - 255, 0, 255);
*resB = CLIP3(2 * mixB + baseB - 255, 0, 255);
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
点光模式
公式如下:
代码如下:
//点光模式
inline void BlendPinLight(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
int res = 0;
int a = mixR + mixR - 255;
int b = mixR + mixR;
if (baseR < a)
res = b - 255;
if (baseR >= a && baseR < b)
res = baseR;
if (baseR > b)
res = b;
*resR = CLIP3(res, 0, 255);
a = mixG + mixG - 255;
b = mixG + mixG;
if (baseG < a)
res = b - 255;
if (baseG >= a && baseG < b)
res = baseG;
if (baseG > b)
res = b;
*resG = CLIP3(res, 0, 255);
a = mixB + mixB - 255;
b = mixB + mixB;
if (baseB < a)
res = b - 255;
if (baseB >= a && baseB < b)
res = baseB;
if (baseB > b)
res = b;
*resB = CLIP3(res, 0, 255);
*resR = CLIP3((*resR * alpha + baseR * (100 - alpha)) / 100, 0, 255);
*resG = CLIP3((*resG * alpha + baseG * (100 - alpha)) / 100, 0, 255);
*resB = CLIP3((*resB * alpha + baseB * (100 - alpha)) / 100, 0, 255);
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
实色混合模式
公式如下:
代码如下:
//实色混合模式
inline void BlendHardMix(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = baseR + mixR < 255 ? 0 : 255;
*resG = baseG + mixG < 255 ? 0 : 255;
*resB = baseB + mixB < 255 ? 0 : 255;
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
差值模式
公式如下:
代码如下:
//差值模式
inline void BlendDifference(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = abs(mixR - baseR);
*resG = abs(mixG - baseG);
*resB = abs(mixB - baseB);
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
排除模式
公式如下:
代码如下:
//排除模式
inline void BlendExclusion(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = CLIP3((mixR + baseR) - mixR * baseR / 128, 0, 255);
*resG = CLIP3((mixG + baseG) - mixG * baseG / 128, 0, 255);
*resB = CLIP3((mixB + baseB) - mixB * baseB / 128, 0, 255);
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
减去模式
公式如下:
代码如下:
//减去模式
inline void BlendSubtract(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = CLIP3(baseR - mixR, 0, 255);
*resG = CLIP3(baseG - mixG, 0, 255);
*resB = CLIP3(baseB - mixB, 0, 255);
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
划分模式
公式如下:
代码如下:
//划分模式
inline void BlendDivide(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
*resR = CLIP3(255 * baseR / (mixR + 1), 0, 255);
*resG = CLIP3(255 * baseG / (mixG + 1), 0, 255);
*resB = CLIP3(255 * baseB / (mixB + 1), 0, 255);
*resR = (*resR * alpha + baseR * (100 - alpha)) / 100;
*resG = (*resG * alpha + baseG * (100 - alpha)) / 100;
*resB = (*resB * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
色相模式
色相模式实际上就是在HSV或者HSB/HSI等颜色空间中,将基色的Hue分量替换为混合色的Hue分量, 然后在将基色转换为RGB颜色空间得到结果RGB值。以HSV颜色空间为例,假设基色图层的HSV分量为(BaseH,BaseS,BaseV),混合色图层的HSV分量为(MixH,MixS,MixV),则公式如下:
代码如下:
//色相模式
inline void BlendHue(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
float baseH = 0, baseS = 0, baseV = 0;
float mixH = 0, mixS = 0, mixV = 0;
f_RGB2HSV(baseR, baseG, baseB, &baseH, &baseS, &baseV);
f_RGB2HSV(mixR, mixG, mixB, &mixH, &mixS, &mixV);
unsigned char R = 0, G = 0, B = 0;
f_HSV2RGB(mixH, baseS, baseV, &R, &G, &B);
*resR = (R * alpha + baseR * (100 - alpha)) / 100;
*resG = (G * alpha + baseG * (100 - alpha)) / 100;
*resB = (B * alpha + baseB * (100 - alpha)) / 100;
};
对比效果如下:
本文效果(alpha=50) PS效果(alpha = 50)
注意:这里的效果差异导致原因是本文所用的HSV算法与PS的算法不同,由于PS的算法不公开 ,所以,我们无法得到一摸一样的效果,但是,算法思路是一致的。下文的饱和度模式也是如此。
饱和度模式
饱和度模式实际上就是在HSV或者HSB/HSI等颜色空间中,将基色的饱和度Saturation分量替换为混合色的Saturation分量, 然后在将基色转换为RGB颜色空间得到结果RGB值。以HSV颜色空间为例,假设基色图层的HSV分量为(BaseH,BaseS,BaseV),混合色图层的HSV分量为(MixH,MixS,MixV),则公式如下:
代码如下:
//饱和度模式
inline void BlendSaturation(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
float baseH = 0, baseS = 0, baseV = 0;
float mixH = 0, mixS = 0, mixV = 0;
f_RGB2HSV(baseR, baseG, baseB, &baseH, &baseS, &baseV);
f_RGB2HSV(mixR, mixG, mixB, &mixH, &mixS, &mixV);
unsigned char R = 0, G = 0, B = 0;
f_HSV2RGB(baseH, mixS, baseV, &R, &G, &B);
*resR = (R * alpha + baseR * (100 - alpha)) / 100;
*resG = (G * alpha + baseG * (100 - alpha)) / 100;
*resB = (B * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
明度模式
明度模式实际上就是在HSV或者HSB/HSI等颜色空间中,将基色的明度Luminosity分量替换为混合色的Luminosity分量, 然后在将基色转换为RGB颜色空间得到结果RGB值。以HSV颜色空间为例,假设基色图层的HSV分量为(BaseH,BaseS,BaseV),混合色图层的HSV分量为(MixH,MixS,MixV),则公式如下:
代码如下:
//亮度模式
inline void BlendLuminosity(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
float baseH = 0, baseS = 0, baseV = 0;
float mixH = 0, mixS = 0, mixV = 0;
f_RGB2HSV(baseR, baseG, baseB, &baseH, &baseS, &baseV);
f_RGB2HSV(mixR, mixG, mixB, &mixH, &mixS, &mixV);
unsigned char R = 0, G = 0, B = 0;
f_HSV2RGB(baseH, baseS, mixV, &R, &G, &B);
*resR = (R * alpha + baseR * (100 - alpha)) / 100;
*resG = (G * alpha + baseG * (100 - alpha)) / 100;
*resB = (B * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
颜色模式
颜色模式实际上就是在HSV或者HSB/HSI等颜色空间中,将基色的H和S分量替换为混合色的H和S分量, 然后在将基色转换为RGB颜色空间得到结果RGB值。以HSV颜色空间为例,假设基色图层的HSV分量为(BaseH,BaseS,BaseV),混合色图层的HSV分量为(MixH,MixS,MixV),则公式如下:
代码如下:
//颜色模式
inline void BlendColor(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int alpha)
{
float baseH = 0, baseS = 0, baseV = 0;
float mixH = 0, mixS = 0, mixV = 0;
f_RGB2HSV(baseR, baseG, baseB, &baseH, &baseS, &baseV);
f_RGB2HSV(mixR, mixG, mixB, &mixH, &mixS, &mixV);
unsigned char R = 0, G = 0, B = 0;
f_HSV2RGB(mixH, mixS, baseV, &R, &G, &B);
*resR = (R * alpha + baseR * (100 - alpha)) / 100;
*resG = (G * alpha + baseG * (100 - alpha)) / 100;
*resB = (B * alpha + baseB * (100 - alpha)) / 100;
};
效果对比如下:
本文效果(alpha=50) PS效果(alpha = 50)
注意:色相模式(Hue)、饱和度模式(Saturation)、颜色模式(Color)、亮度模式(Luminosity)这四种图层混合模式,由于Photoshop并未公开对应颜色空间算法,因此我们只做了算法模拟,使用了公开的HSV算法,并未达到完全一致的效果,但算法原理是一致的。
上述就是Photoshop中27种图层混合算法解析。
代码调用
有了上面的算法实现,我们就可以轻松调用,我们定义一个图层混合调用接口,输入是基色图层的RGB和混合色图层的RGB信息,以及图层混合模式blendModel,透明度alpha,输出是结果色图层的RGB,代码如下:
inline void layerBlend(int baseR, int baseG, int baseB, int mixR, int mixG, int mixB, int* resR, int* resG, int* resB, int blendModel, int alpha)
{
switch (blendModel)
{
case PS_Normal:
BlendNormal(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_Dissolve:
BlendDissolve(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_Darken:
BlendDarken(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_Multiply:
BlendMultiply(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_ColorBurn:
BlendColorBurn(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_LinearBurn:
BlendLinearBurn(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_Dark:
BlendDark(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_Lighten:
BlendLighten(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_Screen:
BlendScreen(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_ColorDodge:
BlendColorDodge(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_LinearDodge:
BlendLinearDodge(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_LighterColor:
BlendLighterColor(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_Overlay:
BlendOverlay(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_SoftLight:
BlendSoftLight(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_HardLight:
BlendHardLight(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_VividLight:
BlendVividLight(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_LinearLight:
BlendLinearLight(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_PinLight:
BlendPinLight(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_HardMix:
BlendHardMix(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_Difference:
BlendDifference(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_Exclusion:
BlendExclusion(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_Subtract:
BlendSubtract(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_Divide:
BlendDivide(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_Hue:
BlendHue(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_Saturation:
BlendSaturation(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_Color:
BlendColor(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
case PS_Luminosity:
BlendLuminosity(baseR, baseG, baseB, mixR, mixG, mixB, resR, resG, resB, alpha);
break;
default:
break;
}
};
最后,我们定义一个图片调用接口如下:
/*********************************************************
*Function:图层混合
*Params:
* baseData:32bgra基底图层图像数据
* bWidth:基底图层图像宽度
* bHeight:基底图层图像高度
* bStride:基底图层图像幅度,对于32bgra格式而言,bStride=bWidth*4
* mixData:32bgra混合图层图像数据
* mWidth:混合图层图像宽度
* mHeight:混合图层图像高度
* mStride:混合图层图像幅度,对于32bgra格式而言,bStride=bWidth*4
* blendModel:图层混合模式
* alpha:透明度,范围[0,100]
*Return: 0-成功,其他失败
*********************************************************/
int f_LayerBlending(unsigned char* baseData, int bWidth, int bHeight, int bStride, unsigned char* mixData, int mWidth, int mHeight, int mStride, int blendModel, int alpha)
{
int ret = 0;
unsigned char* pSrc = baseData;
for (int j = 0; j < bHeight; j++)
{
for (int i = 0; i < bWidth; i++)
{
int baseR = pSrc[2];
int baseG = pSrc[1];
int baseB = pSrc[0];
int cx = i * mWidth / bWidth;
int cy = j * mHeight / bHeight;
int pos = cx * 4 + cy * mStride;
int mixR = mixData[pos + 2];
int mixG = mixData[pos + 1];
int mixB = mixData[pos];
int resR = 0;
int resG = 0;
int resB = 0;
layerBlend(baseR, baseG, baseB, mixR, mixG, mixB, &resR, &resG, &resB, blendModel,alpha);
pSrc[0] = resB;
pSrc[1] = resG;
pSrc[2] = resR;
pSrc += 4;
}
}
return ret;
};
对于任何两张图片,接口的调用代码如下:
#include"imgRW\\f_SF_ImgBase_RW.h"
#include"f_LayerBlendingModels.h"
int _tmain(int argc, _TCHAR* argv[])
{
//定义输入图像路径
char* inputImgPath = "Test.png";
char* inputMixImgPath = "mix.png";
char* outputImgPath = "PS_Color.png";
//定义基底图层图像宽高信息
int bWidth = 0, bHeight = 0, component = 0, bStride = 0;
//图像读取(得到32位bgra格式图像数据)
unsigned char* baseData = Trent_ImgBase_ImageLoad(inputImgPath, &bWidth, &bHeight, &component);
bStride = bWidth * 4;
//定义混合图层图像宽高信息
int mWidth = 0, mHeight = 0, mStride = 0;
//图像读取(得到32位bgra格式图像数据)
unsigned char* mixData = Trent_ImgBase_ImageLoad(inputMixImgPath, &mWidth, &mHeight, &component);
mStride = mWidth * 4;
int blendModel = PS_Color;
int alpha = 50;
int ret = f_LayerBlending(baseData, bWidth, bHeight, bStride, mixData, mWidth, mHeight, mStride, blendModel,alpha);
ret = Trent_ImgBase_ImageSave(outputImgPath, bWidth, bHeight, baseData, PNG);
printf("Done!");
free(baseData);
free(mixData);
return 0;
}
上述代码可以看到,逻辑清晰,非常简单,而且没有依赖任何第三方算法库,完全是C语言实现,非常适合大家入门进阶!
扩展阅读
图层混合的用途非常广,是PS里最常用的功能,对于图像算法而言,目前的图形图像处理app和软件等,都有图层混合算法的应用,涉及到各种滤镜特效,人像美颜美妆等等方面。掌握这些基础内容,对于从事相关工作非常重要。
最后,给出本文所有代码完整工程的下载方式:
关注本人公众号“SF图像算法”,有相关下载链接即可免费下载。
以上是关于C语言数字图像处理进阶---1.1 Photoshop图层算法的主要内容,如果未能解决你的问题,请参考以下文章