小红书颜色滤镜技术分析

3,023 阅读3分钟

背景

小红书是很多年轻人喜欢的一款软件,里面很多漂亮的小姐姐,小姐姐本身很漂亮是事实,但是如果没有优秀的滤镜处理技术,小姐姐们的漂亮要打一个折扣的,从分析小红书的技术这一块可以看出小红书在音视频这一块的投入也是很大的,VE和AE都做了比较不错,我也经常使用小红书,作为一个音视频开发工程师,看到里面的音视频功能,就忍不住想自己上手看看,到底是怎么做的,我们本文就分析小红书的一个图片处理的滤镜——颜色调节模块。

output_1.jpeg

从面的截图可以看出来,小红书的颜色调节共有:亮度、对比度、色温、饱和度、颗粒度五种类型,还有一个锐度,小红书中没有提到,等会我们可以加餐一下。

亮度

  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.a);
  }

通过调整brightness来调节亮度,brightness调节范围是:-1.0f ~ 1.0f,其中0.0f表示原图。

对比度

  varying highp vec2 textureCoordinate;

  uniform sampler2D inputImageTexture;
  uniform lowp float contrast;

  void main() {
    lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
    gl_FragColor = vec4(((textureColor.rgb - vec3(0.5)) * contrast + vec3(0.5)), textureColor.a);
  }

通过调整contrast来调节亮度,contrast调节范围是:0.0f ~ 2.0f,其中1.0f表示原图。

色温

  uniform sampler2D inputImageTexture;
  varying highp vec2 textureCoordinate;

  uniform lowp float temperature;
  uniform lowp float tint;

  const lowp vec3 warmFilter = vec3(0.93, 0.54, 0.0);

  const mediump mat3 RGBtoYIQ = mat3(0.299, 0.587, 0.114, 0.596, -0.274, -0.322, 0.212, -0.523, 0.311);
  const mediump mat3 YIQtoRGB = mat3(1.0, 0.956, 0.621, 1.0, -0.272, -0.647, 1.0, -1.105, 1.702);

  void main() {
    lowp vec4 source = texture2D(inputImageTexture, textureCoordinate);
    mediump vec3 yiq = RGBtoYIQ * source.rgb;
    yiq.b = clamp(yiq.b + tint*0.5226*0.1, -0.5226, 0.5226);
    lowp vec3 rgb = YIQtoRGB * yiq;

    lowp vec3 processed = vec3(
            (rgb.r < 0.5 ? (2.0 * rgb.r * warmFilter.r) : (1.0 - 2.0 * (1.0 - rgb.r) * (1.0 - warmFilter.r))),
            (rgb.g < 0.5 ? (2.0 * rgb.g * warmFilter.g) : (1.0 - 2.0 * (1.0 - rgb.g) * (1.0 - warmFilter.g))),
            (rgb.b < 0.5 ? (2.0 * rgb.b * warmFilter.b) : (1.0 - 2.0 * (1.0 - rgb.b) * (1.0 - warmFilter.b)))
    );

    gl_FragColor = vec4(mix(rgb, processed, temperature), source.a);
  }

色温涉及到RGB和YIQ的转换,就是两种色彩系统的转换,调整色温的temperature范围是2000.0f ~ 8000.0f,5000.0f表示原图,另一个参数hint默认是0.0f。

下面三张图,第一张是原图,第二章是暖色调,第三章是冷色调。

test_1.jpg

test_2.jpg

test_3.jpg

饱和度

  varying highp vec2 textureCoordinate;

  uniform sampler2D inputImageTexture;
  uniform lowp float saturation;

  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.a);
  }

饱和度是指图像色彩的鲜艳程度,通过调节saturation来实现调节饱和度。saturation范围是0.0f ~ 2.0f,其中1.0f表示原图。saturation越大,表示色彩越鲜艳,反之,色彩则越单调。

颗粒度

  varying highp vec2 textureCoordinate;
  uniform sampler2D inputImageTexture;

  /// 调节胶片颗粒感
  uniform lowp float grain;

  void main() {
    lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
    float noise = (fract(sin(dot(textureCoordinate, vec2(12.9898, 78.233) * 2.0)) * 43758.5453));
    gl_FragColor = textureColor - noise * grain
  }

颗粒度实际上是图像本身的噪点变多了,颗粒度越大,说明图像的噪点越多,通过调节grain参数实现调节颗粒度,grain范围是0.0f ~ 0.5f,其中0.0f表示原图。

下面看一下0.5f情况下的颗粒度调整图:整体的颗粒感还是很强的。

test_4.jpg

这张图的原图是:

test_5.jpg

锐度

顶点着色器

  attribute vec4 position;
  attribute vec4 inputTextureCoordinate;

  uniform float imageWidthFactor;
  uniform float imageHeightFactor;
  uniform float sharpness;

  varying vec2 textureCoordinate;
  varying vec2 leftTextureCoordinate;
  varying vec2 rightTextureCoordinate;
  varying vec2 topTextureCoordinate;
  varying vec2 bottomTextureCoordinate;

  varying float centerMultiplier;
  varying float edgeMultiplier;

  void main() {
    gl_Position = position;

    mediump vec2 widthStep = vec2(imageWidthFactor, 0.0);
    mediump vec2 heightStep = vec2(0.0, imageHeightFactor);

    textureCoordinate = inputTextureCoordinate.xy;
    leftTextureCoordinate = inputTextureCoordinate.xy - widthStep;
    rightTextureCoordinate = inputTextureCoordinate.xy + widthStep;
    topTextureCoordinate = inputTextureCoordinate.xy + heightStep;
    bottomTextureCoordinate = inputTextureCoordinate.xy - heightStep;

    centerMultiplier = 1.0 + 4.0 * sharpness;
    edgeMultiplier = sharpness;
  }

片元着色器

  precision highp float;

  varying highp vec2 textureCoordinate;
  varying highp vec2 leftTextureCoordinate;
  varying highp vec2 rightTextureCoordinate;
  varying highp vec2 topTextureCoordinate;
  varying highp vec2 bottomTextureCoordinate;

  varying highp float centerMultiplier;
  varying highp float edgeMultiplier;

  uniform sampler2D inputImageTexture;

  void main() {
    mediump vec3 textureColor = texture2D(inputImageTexture, textureCoordinate).rgb;
    mediump vec3 leftTextureColor = texture2D(inputImageTexture, leftTextureCoordinate).rgb;
    mediump vec3 rightTextureColor = texture2D(inputImageTexture, rightTextureCoordinate).rgb;
    mediump vec3 topTextureColor = texture2D(inputImageTexture, topTextureCoordinate).rgb;
    mediump vec3 bottomTextureColor = texture2D(inputImageTexture, bottomTextureCoordinate).rgb;

    gl_FragColor = vec4((textureColor * centerMultiplier - (leftTextureColor * edgeMultiplier +
            rightTextureColor * edgeMultiplier + topTextureColor * edgeMultiplier +
            bottomTextureColor * edgeMultiplier)), texture2D(inputImageTexture, bottomTextureCoordinate).a);
  }

锐度实际上是检测图片像素边缘的像素点,让整张图片像素点突出,对比非常强烈。锐度调节通过sharpness来实现,调整的范围是-4.0f ~ 4.0f, 0.0f表示原图。

下面是一张锐度风格明显的图片。

test_6.jpg