本文基於開源項目nekocode/CameraFilter(項目地址:https://github.com/nekocode/CameraFilter, 感興趣的讀者可以自行下載該項目代碼,運行於Android系統),講解一些該項目已經實現的濾鏡以及我自己結合一些現有算法在該項目上實現的濾鏡功能。項目本身代碼比較簡單,是基於OpenGL ES實現的,我們可以利用這個項目的代碼快速實現自己設計的算法,不僅侷限於濾鏡,我們這裏泛稱爲濾鏡。
本項目實現的濾鏡,核心的算法基本都是在片元着色器(fragment shader)實現的。這裏相應也是主要講解片元着色器的代碼。下圖簡要說明了片元着色器涉及的輸入輸出參數。
作標註的爲本文涉及的輸入輸出參數, 片元着色器的輸入參數爲texCoord和iChannel0,分別爲片元座標和紋理數據;輸出參數爲gl_FragColor,爲運算之後的texCoord座標下的色值。掌握清楚這裏,對於本文的理解尤爲重要。
- 三合一
void main() {
if (texCoord.y <= 0.333){
gl_FragColor = texture2D(iChannel0, vec2(texCoord.x,texCoord.y + 0.333));
}else 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)從信號處理的角度,圖像處理分爲空間域處理和頻域處理。本文所涉及算法軍位於空間域。
參考資料