本文基于开源项目nekocode/CameraFilter(项目地址:https://github.com/nekocode/CameraFilter, 感兴趣的读者可以自行下载该项目代码,运行于Android系统),讲解一些该项目已经实现的滤镜以及我自己结合一些现有算法在该项目上实现的滤镜功能。项目本身代码比较简单,是基于OpenGL ES实现的,我们可以利用这个项目的代码快速实现自己设计的算法,不仅局限于滤镜,我们这里泛称为滤镜。
本项目实现的滤镜,核心的算法基本都是在片元着色器(fragment shader)实现的。这里相应也是主要讲解片元着色器的代码。下图简要说明了片元着色器涉及的输入输出参数。
作标注的为本文涉及的输入输出参数, 片元着色器的输入参数为texCoord和iChannel0,分别为片元坐标和纹理数据;输出参数为gl_FragColor,为运算之后的texCoord坐标下的色值。掌握清楚这里,对于本文的理解尤为重要。
三合一
void main() {
if (texCoord.y 0.333 && texCoord.y<= 0.666){
gl_FragColor = texture2D(iChannel0, texCoord);
}else{
gl_FragColor = texture2D(iChannel0, vec2(texCoord.x,texCoord.y - 0.333));
}
}
浮雕
const highp vec3 transMatrix = vec3(0.2125, 0.7154, 0.0721);
const vec4 bgColor = vec4(0.5, 0.5, 0.5, 1.0);
void main() {
vec2 currentUV = texCoord;
vec2 preUV = vec2(currentUV.x-5.0/iResolution.x, currentUV.y-5.0/iResolution.y);
vec4 currentMask = texture2D(iChannel0, currentUV);
vec4 preMask = texture2D(iChannel0, preUV);
vec4 delColor = currentMask - preMask;
float luminance = dot(delColor.rgb, transMatrix);
gl_FragColor = vec4(vec3(luminance), 0.0) + bgColor;
}
漩涡(太极图)
const float PI = 3.14159265;
const float rotateRadian = PI/3.0;
const float radiusRatio = 0.8;
const float center = 0.5;
void main() {
float radius = min(iResolution.x,iResolution.y)*radiusRatio/2.0;
vec2 texCoord = texCoord;
vec2 currentUV = texCoord;
currentUV.x *= iResolution.x;
currentUV.y *= iResolution.y;
vec2 centerUV = iResolution.xy * center;
vec2 deltaUV = currentUV - centerUV;
float deltaR = length(deltaUV);
float beta = atan(deltaUV.y, deltaUV.x) + rotateRadian * 2.0 * (-(deltaR/radius)*(deltaR/radius) + 1.0);
vec2 dstUV = currentUV;
if(deltaR <= radius){
dstUV = centerUV + deltaR*vec2(cos(beta), sin(beta));
}
dstUV.x /=iResolution.x;
dstUV.y /=iResolution.y;
gl_FragColor = texture2D(iChannel0, dstUV);
}
熔铸
void main() {
vec4 mask = texture2D(iChannel0, texCoord);
vec4 tempColor = vec4(mask.r*0.5/(mask.g + mask.b + 0.01),
mask.g*0.5/(mask.r + mask.b + 0.01),
mask.b*0.5/(mask.r + mask.g + 0.01),
1.0);
gl_FragColor = tempColor;
}
边缘检测
void main() {
vec4 color = texture2D(iChannel0, texCoord);
float gray = length(color.rgb);
gl_FragColor = vec4(vec3(step(0.06, length(vec2(dFdx(gray), dFdy(gray))))), 1.0);
}
水波
void main() {
float stongth = 0.3;
vec2 uv = texCoord.xy;
float waveu = sin((uv.y + iGlobalTime) * 20.0) * 0.5 * 0.05 * stongth;
gl_FragColor = texture2D(iChannel0, uv + vec2(waveu, 0));
}
随着技术的不断发展,各种各样的传感器被集成到电脑、手机、机器人等设备,结合这些传感器感知的物理信息以及图像识别等算法,我们可以实现更多功能强大并且实时的特效。
比如,近几年比较火的小视频应用抖音或者快手,有上百种特效。以下示意图,是抖音的几种特效,需要结合人脸识别或者景深测量,其余替换像素的实现原理应该和我今天所讲的类似。这些特效,如果是应用于静态拍照的话,应该还是比较容易的;其难点在于在视频这种动态的情景下,仍然能够达到很好的实时性,看不出明显的特效的延时或者其他瑕疵,并且功耗较低,性能优化的非常好。
总之,理解并掌握最基本的图像处理原理和实现方法还是很重要的,基于这些原理和方法可以制作出千变万化的效果。
注意:
(1)本例中坐标和色值都是归一化(normalized)的,取值范围均为[0,1].
(2)从信号处理的角度,图像处理分为空间域处理和频域处理。本文所涉及算法军位于空间域。
参考资料
https://www.jianshu.com/p/a771639ffbbb
https://github.com/nekocode/CameraFilter
作者:悟得笑