GPUImage内置了很多调节色彩的Filter,这些Filter的实现都是相似的,唯一不同的是片元着色器的实现,本文主要分析下片元着色器的代码。
GPUImageBrightnessFilter
GPUImageBrightnessFilter主要是调节图像亮度。片元着色器代码如下:
NSString *const kGPUImageBrightnessFragmentShaderString = SHADER_STRING
(
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform lowp float brightness;
void main()
{
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
gl_FragColor = vec4((textureColor.rgb + vec3(brightness)), textureColor.w);
}
);
代码非常简单,只是“简单粗暴”的对rgb三个通道同时加上输入的brightness值。brightness的范围是[-1.0, 1.0]。下面是效果图:
由图可见,当brightness小于0时图像变暗,大于0时图像变亮。也可以看出,因色彩的范围是[0, 1], 对原图加上一个亮度值后,会造成一部分颜色超出[0, 1]范围,虽然超出的部分会被限制在0和1上,但也造成了部分细节的丢失。
GPUImageExposureFilter
GPUImageExposureFilter调节图像的曝光度。片元着色器代码如下:
NSString *const kGPUImageExposureFragmentShaderString = SHADER_STRING
(
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform highp float exposure;
void main()
{
highp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
gl_FragColor = vec4(textureColor.rgb * pow(2.0, exposure), textureColor.w);
}
);
可以看出和GPUImageBrightnessFilter类似,也是同时调节RGB三个通道的值,只不过GPUImageExposureFilter是用指数函数,公式如下:
GPUImageContrastFilter
GPUImageContrastFilter调节图像的对比度。着色器代码如下:
NSString *const kGPUImageContrastFragmentShaderString = SHADER_STRING
(
varying vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform float contrast;
void main()
{
vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
gl_FragColor = vec4(((textureColor.rgb - vec3(0.5)) * contrast + vec3(0.5)), textureColor.w);
}
);
可看到输入和输出的关系为:
outRGB = (inRGB - 0.5) * contrast + 0.5
由公式可以看出,对比度调节会改变inRGB和颜色中间值0.5的“距离”。当contrast=1时(默认值),outRGB=inRGB,即原图;当contrast>1时,输出的颜色值会“远离”中值0.5,向两边靠拢,图像的效果就是,“亮的更亮,暗的更暗”;反之,当contrast<1时,输出值会“靠近”中值0.5,图像上的所有像素的亮度都会趋于相同。效果图如下:
对比度调节同样会有损失细节的问题。例如,当contrast=2时,由公式可知,所有小于0.25的inRGB值都输出0,所有大于0.75的inRGB值都输出1,下面是
contrast调到2和4时的效果:
GPUImageSaturationFilter
GPUImageSaturationFilter调节图像的饱和度。着色器代码如下:
NSString *const kGPUImageSaturationFragmentShaderString = SHADER_STRING
(
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform lowp float saturation;
// Values from "Graphics Shaders: Theory and Practice" by Bailey and Cunningham
const mediump vec3 luminanceWeighting = vec3(0.2125, 0.7154, 0.0721);
void main()
{
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
lowp float luminance = dot(textureColor.rgb, luminanceWeighting);
lowp vec3 greyScaleColor = vec3(luminance);
gl_FragColor = vec4(mix(greyScaleColor, textureColor.rgb, saturation), textureColor.w);
}
);
我们看到它首先求出图像的亮度:
luminance = r * 0.2125 + g * 0.7154 + b * 0.0721;
注意rgb变换公式的权重值是归一化的,即0.2125 + 0.7154 + 0.0721 = 1.
然后构造灰度校准向量:
greyScaleColor = vec3(luminance, luminance, luminance);
然后:
outRGB = (1 - saturation) * greyScaleColor + saturation * inRGB;
调节饱和度的算法有很多,一般是先把rgb转换到HSL,再单独调节S分量。对于这里所用的方法,下面是我个人的理解,可能不够准确。
饱和度即色彩的纯度,R, G, B三个分量,如果它们之间的值相差很大,如RGB为(255,0,0),则画面是很鲜明的红色;如果它们的值趋于相同,如(100,100,100),那图像看起来是比较灰的,只有亮度信息而没有色彩信息。上面的公式,inRGB代表的是RGB三个分量的“差异程度”,而greyScaleColor代表RGB三个分量的“趋同程度”,saturation调节它们所占的比重。当saturation < 1时,greyScaleColor灰度信息比重变大,图像饱和度减少,当saturation > 1时,inRGB比重变大,RGB的“差异程度”变大,图像色彩变得更加鲜明。如下图:
GPUImageLevelsFilter
GPUImageLevelsFilter调节图像色阶,着色器代码如下:
#define LevelsControlInputRange(color, minInput, maxInput) min(max(color - minInput, vec3(0.0)) / (maxInput - minInput), vec3(1.0))
#define LevelsControlInput(color, minInput, gamma, maxInput) GammaCorrection(LevelsControlInputRange(color, minInput, maxInput), gamma)
#define LevelsControlOutputRange(color, minOutput, maxOutput) mix(minOutput, maxOutput, color)
#define LevelsControl(color, minInput, gamma, maxInput, minOutput, maxOutput) LevelsControlOutputRange(LevelsControlInput(color, minInput, gamma, maxInput), minOutput, maxOutput)
NSString *const kGPUImageLevelsFragmentShaderString = SHADER_STRING
(
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform mediump vec3 levelMinimum;
uniform mediump vec3 levelMiddle;
uniform mediump vec3 levelMaximum;
uniform mediump vec3 minOutput;
uniform mediump vec3 maxOutput;
void main()
{
mediump vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
gl_FragColor = vec4(LevelsControl(textureColor.rgb, levelMinimum, levelMiddle, levelMaximum, minOutput, maxOutput), textureColor.a);
}
);
为了清晰,把宏展开,并忽略掉min和max的限制的判断后,调节色阶的公式大致为:
具体色阶调节的原理可参看其他资料,它也是一种调节亮度的方法,不再细说,效果如下:
网友评论