前言

本专栏为《零基础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 Photoshop图层算法相关推荐

  1. C语言数字图像处理进阶---9 马赛克滤镜

    [马赛克滤镜] 马赛克滤镜是一种图像处理算法,视觉上看就是通过算法将图像或者视频中的细节模糊,使画面看上去是由一个个的小格子组成,形象的称这种画面为马赛克,这种算法成为马赛克滤镜.效果举例如下:   ...

  2. C语言数字图像处理进阶---13 Ins1977滤镜

    Ins1977滤镜 本文介绍1977这个滤镜的具体实现,这个滤镜最早是Instagram中使用的 ,由于Instagram滤镜备受欢迎,因此,早期的美图秀秀中也使用了这个滤镜,只是名字不是1977,而 ...

  3. C语言数字图像处理进阶---8 Glow Filter

    [Glow Filter发光滤镜] Glow Filter发光滤镜是一种让图像产生朦胧的发光效果的滤镜,效果如下:   原图                                      ...

  4. C语言数字图像处理进阶---15 油画滤镜

    油画滤镜(Oilpaint) 油画(an oil painting:a painting in oils)是以用快干性的植物油调和颜料,在画布亚麻布,纸板或木板上进行制作的一个画种.作画时使用的稀释剂 ...

  5. C语言数字图像处理进阶---14 晕角滤镜

    晕角(Halo)滤镜 晕角滤镜是一种常见的图片修饰手法,表现出来就是给图像四个角添加暗影效果,这暗影向图像中心逐渐淡化.在各种主流图像处理软件中,都经常存在,而且备受欢迎.比如Instagram中有很 ...

  6. C语言数字图像处理进阶---12光照特效滤镜

    光照特效滤镜 光照特效滤镜是一种模拟光源照射物体表面的特效滤镜,如下图所示: 原图                                                     光照滤镜 [ ...

  7. C语言数字图像处理进阶---3连环画风格滤镜

    本小节介绍一款连环画风格的滤镜算法. [连环画风格滤镜] 连环画是小时候经常看的一种令人爱不释手的小书,是70后80后的经典回忆.或许,现在的小孩子对于连环画会比较陌生,本小节将给大家带来一点回忆的味 ...

  8. C语言数字图像处理进阶---18 图像形变滤镜

    图像形变滤镜 图像形变滤镜代表的是一类滤镜,这类滤镜的特点就是像素位置发生了形变,通过位置形变来达到某种特效,比如哈哈镜,鱼眼镜头特效等等. 本文给大家介绍四款形变滤镜:波浪形变滤镜(WaveFilt ...

  9. C语言数字图像处理进阶---4怀旧(老照片)滤镜

    本文介绍一款怀旧风格滤镜特效的代码实现,这个滤镜效果也就是我们常说的老照片滤镜效果. [怀旧滤镜] 说起老照片,大家都很熟悉,谁家还没有几张老照片呢,我们举例如下图所示: 对于上述老照片效果,我们进行 ...

  10. C语言数字图像处理进阶---7 Sketch Filter

    [(Sketch Filter)素描滤镜] 素描是绘画的基础,是使用单一色彩表现明度变化的绘画.由木炭,铅笔,钢笔等,以线条来画出物象明暗的单色画就称作素描. 素描形式多样,以铅笔素描为例,如下图所示 ...

最新文章

  1. 第八周实践项目6 猴子选大王(数组版)
  2. 【深度学习】网络中隐含层神经元节点的个数(需要学习的特征数目)
  3. u检验、t检验、F检验、X2检验 (转)
  4. jquery部分方法
  5. 深度学习(02)-- ANN学习
  6. python 把xml中含有特殊字段的部分提取出来_Python: 爬虫网页解析工具lxml.html(一)...
  7. c语言第一次作业程序题pta,c语言第一次作业程序题pta
  8. php判断当前浏览器是否微信浏览器
  9. 昆仑通态触摸屏如何把参数由触摸屏传递到PLC_深圳PLC自动化培训哪家比较好
  10. 容迟网络中的路由算法笔记(二)
  11. win7工作组看不到“其他电脑”的解决方法
  12. oracle12c关闭pdb,oracle 12c pdb启动与关闭
  13. 2011年20大3D网站
  14. selenium/requess爬取京东手机商品的详细信息1~selenium练习版
  15. 世界各国领土面积排行(第二个版本)
  16. java物流源码_Java物流配货网源码(含数据库).zip
  17. android x86 6,android x86 6.0 build iso
  18. 使用 ipmi 进行远程控制服务器(重装系统、开关机)
  19. uart接口介绍和认识
  20. 栈溢出攻击c语言_软件漏洞分析入门(四)初级栈溢出C_修改程序流程

热门文章

  1. ROS——发布摄像头节点并编写opencv图像处理节点(python)
  2. 达梦数据库在ZYJ环境上通过RPM打包注册服务的步骤
  3. 验证码输入错误后自动刷新验证码
  4. GIS空间分析(二)—— 空间分析的历史与发展
  5. Tensorflow系列——Saver的用法
  6. Facebook Libra币开发指南---Move语言开发代币智能合约
  7. win10企业版2016长期服务版本认证过期怎么办?
  8. edp协议 netty_使用esp8266 arduino 通过EDP协议 将数据传递到onenet平台
  9. 用PS把人物皮肤处理的质感又通透
  10. 企业微信邮箱可以移动办公吗?