美颜滤镜的CoreImage版本滤镜实现

美颜滤镜的CoreImage版本滤镜实现

本文是对下面文章中的美颜滤镜的CoreImage kernel filter的滤镜部分移植实现。
实战分享:实时美颜滤镜是怎样炼成的

原文采用GPU Image实现。
主要用到了双边滤波、Canny 边缘检测、图片合并。

双边滤波滤镜实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// image : 输入图片
// step :步进方向。(1.0, 0.0)水平方向, (0.0, 1.0) 垂直方向。
// 双边滤波需要使用该算法对图片水平垂直方向各应用一次。
kernel vec4 coreImageKernel(sampler image, vec2 step)
{
const int size = 9;
vec2 coordBlur[size];
vec2 singleStepOffset = step;
int multiplier = 0;
vec2 blurStep;
vec2 pos = destCoord();

for(int i=0; i<size; i++) {
multiplier = (i - (size - 1) / 2);
blurStep = float(multiplier) * singleStepOffset;
coordBlur[i] = pos + blurStep;
}

float disFromColorCenter;
float distanceNormalizationFactor = 4.0;

vec4 sum = vec4(0.0);
float gaussWeightSum = 0.0;
float gaussWeight = 0.18;

vec4 colorCenter = sample(image, samplerTransform(image, coordBlur[4]));
sum += colorCenter * gaussWeight;
gaussWeightSum += gaussWeight;

vec4 colorSample = sample(image, samplerTransform(image, coordBlur[0]));
disFromColorCenter = min(distance(colorCenter, colorSample) * distanceNormalizationFactor, 1.0);
gaussWeight = 0.05 * (1.0 - disFromColorCenter);
sum += colorSample * gaussWeight;
gaussWeightSum += gaussWeight;


colorSample = sample(image, samplerTransform(image, coordBlur[2]));
disFromColorCenter = min(distance(colorCenter, colorSample) * distanceNormalizationFactor, 1.0);
gaussWeight = 0.12 * (1.0 - disFromColorCenter);
gaussWeightSum += gaussWeight;
sum += colorSample * gaussWeight;

colorSample = sample(image, samplerTransform(image, coordBlur[3]));
disFromColorCenter = min(distance(colorCenter, colorSample) * distanceNormalizationFactor, 1.0);
gaussWeight = 0.15 * (1.0 - disFromColorCenter);
gaussWeightSum += gaussWeight;
sum += colorSample * gaussWeight;

colorSample = sample(image, samplerTransform(image, coordBlur[5]));
disFromColorCenter = min(distance(colorCenter, colorSample) * distanceNormalizationFactor, 1.0);
gaussWeight = 0.15 * (1.0 - disFromColorCenter);
gaussWeightSum += gaussWeight;
sum += colorSample * gaussWeight;

colorSample = sample(image, samplerTransform(image, coordBlur[6]));
disFromColorCenter = min(distance(colorCenter, colorSample) * distanceNormalizationFactor, 1.0);
gaussWeight = 0.12 * (1.0 - disFromColorCenter);
gaussWeightSum += gaussWeight;
sum += colorSample * gaussWeight;

colorSample = sample(image, samplerTransform(image, coordBlur[7]));
disFromColorCenter = min(distance(colorCenter, colorSample) * distanceNormalizationFactor, 1.0);
gaussWeight = 0.09 * (1.0 - disFromColorCenter);
gaussWeightSum += gaussWeight;
sum += colorSample * gaussWeight;

colorSample = sample(image, samplerTransform(image, coordBlur[8]));
disFromColorCenter = min(distance(colorCenter, colorSample) * distanceNormalizationFactor, 1.0);
gaussWeight = 0.05 * (1.0 - disFromColorCenter);
gaussWeightSum += gaussWeight;
sum += colorSample * gaussWeight;


vec4 color = sum / float(gaussWeightSum);
return color;

}

Canny Edge 滤镜

Canny 实质上是个组合滤镜。包括GrayscaleFilter、SingleComponentGaussianBlurFilter、DirectionalSobelEdgeDetectionFilter、DirectionalNonMaximumSuppressionFilter、WeakPixelInclusionFilter。

灰度滤镜
1
2
3
4
5
6
7
8
9
//GPU Image中采用侧参数为:0.2125, 0.7154, 0.0721

kernel vec4 coreImageKernel(sampler image)
{
vec4 color = sample(image, samplerCoord(image));
float gray = color.r * 0.299 + color.g * 0.587 + color.b * 0.114;

return vec4(gray, gray, gray, 1.0);
}
SingleComponentGaussianBlurFilter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// image : 输入图片
// step :步进方向。(1.0, 0.0)水平方向, (0.0, 1.0) 垂直方向。
// 高斯模糊需要使用该算法对图片水平垂直方向各应用一次。
kernel vec4 coreImageKernel(sampler image, vec2 step)
{
vec2 pos = destCoord ();
vec2 coordBlur[5];
vec2 singleStepOffset = step;
float sum = 0.0;

coordBlur[0] = pos;
coordBlur[1] = pos + singleStepOffset * 1.407333;
coordBlur[2] = pos - singleStepOffset * 1.407333;
coordBlur[3] = pos + singleStepOffset * 3.294215;
coordBlur[4] = pos - singleStepOffset * 3.294215;

sum += sample(image, samplerTransform(image, coordBlur[0])).r * 0.204164;
sum += sample(image, samplerTransform(image, coordBlur[1])).r * 0.304005;
sum += sample(image, samplerTransform(image, coordBlur[2])).r * 0.304005;
sum += sample(image, samplerTransform(image, coordBlur[3])).r * 0.093913;
sum += sample(image, samplerTransform(image, coordBlur[4])).r * 0.093913;

vec4 color = vec4(sum, sum, sum, 1.0);
//color.a = 1.0;
return color;
}
DirectionalSobelEdgeDetectionFilter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
kernel vec4 coreImageKernel(sampler image)
{
float gray[9];
vec2 gradientDirection;
int i = 0;
for(int row = -1; row <= 1; row++) {
for(int col=-1; col<=1; col++) {
gray[i] = sample(image, samplerTransform(image, destCoord() + vec2(row, col))).r;
i++;
}
}
gradientDirection.x = -gray[6] - 2.0 * gray[3] - gray[0] + gray[2] + 2.0 * gray[5] + gray[8];
gradientDirection.y = -gray[0] - 2.0 * gray[1] - gray[2] + gray[6] + 2.0 * gray[7] + gray[8];
float gradientMagnitude = length(gradientDirection);
vec2 normalizedDirection = normalize(gradientDirection);
normalizedDirection = sign(normalizedDirection) * floor(abs(normalizedDirection) + 0.617316); // Offset by 1-sin(pi/8) to set to 0 if near axis, 1 if away
normalizedDirection = (normalizedDirection + 1.0) * 0.5; // Place -1.0 - 1.0 within 0 - 1.0

vec4 color = vec4(gradientMagnitude, normalizedDirection.x, normalizedDirection.y, 1.0);

return color;
}
DirectionalNonMaximumSuppressionFilter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
kernel vec4 coreImageKernel(sampler image)
{
vec2 pos = destCoord ();
const float lowerThreshold = 0.1;
const float upperThreshold = 0.5;
vec3 currentGradientAndDirection = sample(image, samplerCoord(image)).rgb;
vec2 gradientDirection = ((currentGradientAndDirection.gb * 2.0) - 1.0) * vec2(1.0, 1.0);

float firstSampledGradientMagnitude = sample(image, samplerTransform(image, pos + gradientDirection)).r;
float secondSampledGradientMagnitude = sample(image, samplerTransform(image, pos - gradientDirection)).r;

float multiplier = step(firstSampledGradientMagnitude, currentGradientAndDirection.r);
multiplier = multiplier * step(secondSampledGradientMagnitude, currentGradientAndDirection.r);

float thresholdCompliance = smoothstep(lowerThreshold, upperThreshold, currentGradientAndDirection.r);
multiplier = multiplier * thresholdCompliance;

//sum += sample(image, samplerTransform(image, coordBlur[0])).r * 0.204164;

vec4 color = vec4(multiplier, multiplier, multiplier, 1.0);

return color;
}
WeakPixelInclusionFilter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
kernel vec4 coreImageKernel(sampler image)
{
vec2 pos = destCoord ();
float sum = 0.0;
vec2 pos1;
for(int i=-1; i<=1; i++) {
for(int j=-1; j<=1; j++) {
pos1 = pos + vec2(float(i), float(j));
sum += sample(image, samplerTransform(image, pos1)).r;
}
}

float sumTest = step(1.5, sum);
float pixelTest = step(0.01, sample(image, samplerCoord(image)).r);

vec4 color = vec4(pixelTest, pixelTest, pixelTest, 1.0);

return color;
}

图片合并滤镜

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
* imageOrigin : 原始图片
* imageBlur : 原始图片的双边滤波结果
* imageEdge :原始图片的边缘检测结果
* smoothDegree : 平滑度, 纸越大越平滑
*/

kernel vec4 coreImageKernel(sampler imageOrigin, sampler imageBlur, sampler imageEdge, float smoothDegree)
{
highp vec4 origin = sample(imageOrigin, samplerCoord(imageOrigin));
highp vec4 bilateral = sample(imageBlur, samplerCoord(imageBlur));
highp vec4 canny = sample(imageEdge, samplerCoord(imageEdge));

highp vec4 smooth1;

lowp float r = origin.r;
lowp float g = origin.g;
lowp float b = origin.b;
if (canny.r < 0.2 && r > 0.3725 && g > 0.1568 && b > 0.0784 && r > b && (max(max(r, g), b) - min(min(r, g), b)) > 0.0588 && abs(r-g) > 0.0588) {
smooth1 = (1.0 - smoothDegree) * (origin - bilateral) + bilateral;
}
else {
smooth1 = origin;
}
smooth1.r = log(1.0 + 0.2 * smooth.r)/log(1.2);
smooth1.g = log(1.0 + 0.2 * smooth.g)/log(1.2);
smooth1.b = log(1.0 + 0.2 * smooth.b)/log(1.2);
vec4 color = smooth1;
//color = sample(image, samplerCoord(image));
return color;

}