precision highp float;
varying highp vec2 textureCoordinate;
uniform sampler2D inputimageTexture;
uniform mediump float hueAdjust;
const highp vec4 kRGBToYPrime = vec4 (0.299,0.587,0.114,0.0);
const highp vec4 kRGBToI = vec4 (0.595716,-0.274453,-0.321263,0.0);
const highp vec4 kRGBToQ = vec4 (0.211456,-0.522591,0.31135,0.0);
const highp vec4 kYIQToR = vec4 (1.0,0.9563,0.6210,0.0);
const highp vec4 kYIQToG = vec4 (1.0,-0.2721,-0.6474,0.0);
const highp vec4 kYIQToB = vec4 (1.0,-1.1070,1.7046,0.0);
void main ()
{
// Sample the input pixel
highp vec4 color = texture2D(inputimageTexture,textureCoordinate);
// Convert to YIQ
highp float YPrime = dot (color,kRGBToYPrime);
highp float I = dot (color,kRGBToI);
highp float Q = dot (color,kRGBToQ);
// Calculate the hue and chroma
highp float hue = atan (Q,I);
highp float chroma = sqrt (I * I + Q * Q);
// Make the user's adjustments
hue += (-hueAdjust); //why negative rotation?
// Convert back to YIQ
Q = chroma * sin (hue);
I = chroma * cos (hue);
// Convert back to RGB
highp vec4 yIQ = vec4 (YPrime,I,Q,0.0);
color.r = dot (yIQ,kYIQToR);
// --> color.g = dot (yIQ,kYIQToG);
// --> color.b = dot (yIQ,kYIQToB);
// Save the result
gl_FragColor = color;
}
);
但是,这张照片就是灰色/蓝色,被冲洗掉或是紫色的绿色.我在正确的轨道上吗?如果没有,如何修改此过滤器以影响个别渠道,同时保留其他渠道?
一些例子:
原来,我试图实现的效果:
(第二个图像几乎没有什么不同,然而红色通道的色调已经变得更加粉红色了,我需要能够在粉红色的橙色之间进行调整).
但是,这里是我得到的B和G评论:
(左侧:<0º,右侧:>0º) 它看起来像我不喜欢以红色的方式影响红色的色调;可能我正在接近这个错误,或者如果我在正确的轨道上,这段代码没有正确调整红色通道色调? (我也尝试使用GPUImageColorMatrixFilter来实现这个效果,但是我并没有得到很多). 编辑:这是我现在使用@ VB_overflow的代码GPUImage包装器着色器的迭代,其功能上影响输入图像的方式类似于我的目标:
#import "GPUImageSkinToneFilter.h"
@implementation GPUImageSkinToneFilter
Nsstring *const kGPUImageSkinToneFragmentShaderString = SHADER_STRING
(
varying highp vec2 textureCoordinate;
uniform sampler2D inputimageTexture;
// [-1;1] <=> [pink;orange]
uniform highp float skinToneAdjust; // will make reds more pink
// Other parameters
uniform mediump float skinHue;
uniform mediump float skinHueThreshold;
uniform mediump float maxHueShift;
uniform mediump float maxSaturationShift;
// RGB <-> HSV conversion,thanks to http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
highp vec3 rgb2hsv(highp vec3 c)
{
highp vec4 K = vec4(0.0,-1.0 / 3.0,2.0 / 3.0,-1.0);
highp vec4 p = mix(vec4(c.bg,K.wz),vec4(c.gb,K.xy),step(c.b,c.g));
highp vec4 q = mix(vec4(p.xyw,c.r),vec4(c.r,p.yzx),step(p.x,c.r));
highp float d = q.x - min(q.w,q.y);
highp float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)),d / (q.x + e),q.x);
}
// HSV <-> RGB conversion,thanks to http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
highp vec3 hsv2rgb(highp vec3 c)
{
highp vec4 K = vec4(1.0,1.0 / 3.0,3.0);
highp vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx,clamp(p - K.xxx,0.0,1.0),c.y);
}
// Main
void main ()
{
// Sample the input pixel
highp vec4 colorRGB = texture2D(inputimageTexture,textureCoordinate);
// Convert color to HSV,extract hue
highp vec3 colorHSV = rgb2hsv(colorRGB.rgb);
highp float hue = colorHSV.x;
// check how far from skin hue
highp float dist = hue - skinHue;
if (dist > 0.5)
dist -= 1.0;
if (dist < -0.5)
dist += 1.0;
dist = abs(dist)/0.5; // normalized to [0,1]
// Apply Gaussian like filter
highp float weight = exp(-dist*dist*skinHueThreshold);
weight = clamp(weight,1.0);
// We want more orange,so increase saturation
if (skinToneAdjust > 0.0)
colorHSV.y += skinToneAdjust * weight * maxSaturationShift;
// we want more pinks,so decrease hue
else
colorHSV.x += skinToneAdjust * weight * maxHueShift;
// final color
highp vec3 finalColorRGB = hsv2rgb(colorHSV.rgb);
// display
gl_FragColor = vec4(finalColorRGB,1.0);
}
);
#pragma mark -
#pragma mark Initialization and teardown
@synthesize skinToneAdjust;
@synthesize skinHue;
@synthesize skinHueThreshold;
@synthesize maxHueShift;
@synthesize maxSaturationShift;
- (id)init
{
if(! (self = [super initWithFragmentShaderFromString:kGPUImageSkinToneFragmentShaderString]) )
{
return nil;
}
skinToneAdjustUniform = [filterProgram uniformIndex:@"skinToneAdjust"];
skinHueUniform = [filterProgram uniformIndex:@"skinHue"];
skinHueThresholdUniform = [filterProgram uniformIndex:@"skinHueThreshold"];
maxHueShiftUniform = [filterProgram uniformIndex:@"maxHueShift"];
maxSaturationShiftUniform = [filterProgram uniformIndex:@"maxSaturationShift"];
self.skinHue = 0.05;
self.skinHueThreshold = 50.0;
self.maxHueShift = 0.14;
self.maxSaturationShift = 0.25;
return self;
}
#pragma mark -
#pragma mark Accessors
- (void)setSkinToneAdjust:(CGFloat)newValue
{
skinToneAdjust = newValue;
[self setFloat:newValue forUniform:skinToneAdjustUniform program:filterProgram];
}
- (void)setSkinHue:(CGFloat)newValue
{
skinHue = newValue;
[self setFloat:newValue forUniform:skinHueUniform program:filterProgram];
}
- (void)setSkinHueThreshold:(CGFloat)newValue
{
skinHueThreshold = newValue;
[self setFloat:newValue forUniform:skinHueThresholdUniform program:filterProgram];
}
- (void)setMaxHueShift:(CGFloat)newValue
{
maxHueShift = newValue;
[self setFloat:newValue forUniform:maxHueShiftUniform program:filterProgram];
}
- (void)setMaxSaturationShift:(CGFloat)newValue
{
maxSaturationShift = newValue;
[self setFloat:newValue forUniform:maxSaturationShiftUniform program:filterProgram];
}
@end
解决方法
经过一些实验,在我看来,红色的色调要更加“粉红色”,你需要减少色调,但要获得更多的“橙色”,你需要增加饱和度.
在代码中,我转换为HSV而不是YIQ,因为它更快,使调整饱和成为可能,仍然允许调整色调. HSV组件也在[0-1]间隔,所以不需要处理弧度.
所以这里是这样做的:
>您选择参考色调或颜色(在您的情况下为红色色调)
着色器计算从当前像素色调到参考色调的“距离”
>根据这个距离,如果你想粉红色,减少色调,如果你想要橙色增加饱和度
>重要的是要注意,色调的表现与饱和度和值的不同:它应该被视为一个角度(更多信息here).
参考色调应该被硬编码,由用户(通过颜色选择图像)选择,或者通过分析图像内容来找到.
在计算距离方面有许多不同的可能方法,在这个例子中,我选择使用色相之间的角距离.
您也需要在计算距离之后应用某种过滤,以“选择”最近的颜色,如gaussian like function.
这里是代码,没有ShaderToy的东西:
precision highp float;
// [-1;1] <=> [pink;orange]
const float EFFECT_AMOUNT = -0.25; // will make reds more pink
// Other parameters
const float SKIN_HUE = 0.05;
const float SKIN_HUE_TOLERANCE = 50.0;
const float MAX_HUE_SHIFT = 0.04;
const float MAX_SATURATION_SHIFT = 0.25;
// RGB <-> HSV conversion,thanks to http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
vec3 rgb2hsv(vec3 c)
{
vec4 K = vec4(0.0,-1.0);
vec4 p = mix(vec4(c.bg,c.g));
vec4 q = mix(vec4(p.xyw,c.r));
float d = q.x - min(q.w,q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)),q.x);
}
// HSV <-> RGB conversion,thanks to http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
vec3 hsv2rgb(vec3 c)
{
vec4 K = vec4(1.0,3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx,c.y);
}
// Main
void main ()
{
// Sample the input pixel
vec4 colorRGB = texture2D(inputimageTexture,textureCoordinate);
// get effect amount to apply
float skin_tone_shift = EFFECT_AMOUNT;
// Convert color to HSV,extract hue
vec3 colorHSV = rgb2hsv(colorRGB.rgb);
float hue = colorHSV.x;
// check how far from skin hue
float dist = hue - SKIN_HUE;
if (dist > 0.5)
dist -= 1.0;
if (dist < -0.5)
dist += 1.0;
dist = abs(dist)/0.5; // normalized to [0,1]
// Apply Gaussian like filter
float weight = exp(-dist*dist*SKIN_HUE_TOLERANCE);
weight = clamp(weight,so increase saturation
if (skin_tone_shift > 0.0)
colorHSV.y += skin_tone_shift * weight * MAX_SATURATION_SHIFT;
// we want more pinks,so decrease hue
else
colorHSV.x += skin_tone_shift * weight * MAX_HUE_SHIFT;
// final color
vec3 finalColorRGB = hsv2rgb(colorHSV.rgb);
// display
gl_FragColor = vec4(finalColorRGB,1.0);
}
更多橙色:
更多粉色:
– 编辑 –
在我看来,您没有在ObjectiveC代码中设置统一值.如果你忘记了这个着色器,所有这些都将为零.
代码应如下所示:
- (id)init
{
if(! (self = [super initWithFragmentShaderFromString:kGPUImageSkinToneFragmentShaderString]) )
{
return nil;
}
skinToneAdjustUniform = [filterProgram uniformIndex:@"skinToneAdjust"];
[self setFloat:0.5 forUniform:skinToneAdjustUniform program:filterProgram]; // here 0.5 so should increase saturation
skinHueUniform = [filterProgram uniformIndex:@"skinHue"];
self.skinHue = 0.05;
[self setFloat:self.skinHue forUniform:skinHueUniform program:filterProgram];
skinHuetoleranceUniform = [filterProgram uniformIndex:@"skinHuetolerance"];
self.skinHuetolerance = 50.0;
[self setFloat:self.skinHuetolerance forUniform:skinHuetoleranceUniform program:filterProgram];
maxHueShiftUniform = [filterProgram uniformIndex:@"maxHueShift"];
self.maxHueShift = 0.04;
[self setFloat:self.maxHueShift forUniform:maxHueShiftUniform program:filterProgram];
maxSaturationShiftUniform = [filterProgram uniformIndex:@"maxSaturationShift"];
self.maxSaturationShift = 0.25;
[self setFloat:self.maxSaturationShift forUniform:maxSaturationShiftUniform program:filterProgram];
return self;
}
@end