當前位置: 妍妍網 > 碼農

Android OpenGL 實作「人像背景虛化」效果

2024-03-22碼農

手機上的人像模式, 也被人們稱作「背景虛化」或 」雙攝虛化「 模式,也稱為 Bokeh 模式,能夠在保持畫面中指定的人或物體清晰的同時,將其他的背景模糊掉。 突出畫面的主體部份,主觀上美感更強烈。

VIVO 手機人像模式效果

人像模式的一般實作原理是, 利用雙攝系統獲取景深資訊 並透過深度傳感器和影像分割技術準確分離主題與背景 ,隨後套用人像增強處理和背景虛化演算法,例如美顏、膚色最佳化以及基於深度資訊的虛化等,最終提供清晰突出的人像照片。

所以,人像模式的實作對於軟體層面來說,關鍵還是有能精確輸出帶有深度(景深)資訊的影像分割演算法。

隨著深度學習的發展,現在已經可以做到透過單個網路攝影機,推測場景的深度圖,推薦一個 Deep-Image-Matting 開源模型:
https://github.com/foamliu/Deep-Image-Matting

如果只是簡單的實作人像 Matting , 可以參考飛鴿傳書的開源計畫 ncnn_Android_RobustVideoMatting ,它使用的是騰訊的 ncnn 神經網路計算框架,ncnn 是一個為手機端極致最佳化的高效能神經網路前向計算框架。

https://github.com/githubhaohao/AndroidVideoMatting

接下來,本文將教您如何利用人像分割和 OpenGL 的濾鏡來實作人像背景虛化效果。

照例先上效果圖,OpenGL 實作的「人像背景虛化」效果

實作原理

「人像背景虛化」效果實作,首先獲取到人像的 mask 圖,然後基於這個 mask 圖對人像進行保護, 對背景做一些模糊(虛化)和一些高光的「光斑」效果 ,因為真實的光學系統在大光圈成像時,會在某些高光像素處形成特別質感的光斑。

景深資訊

如果我們拿到的 mask 圖包含了景深資訊的話,我們還可以進行更加細致的處理,比如光斑的近大遠小,虛化效果的層次感,會更加接近手機人像模式成片的效果。

「光斑」效果的實作參考我之前的文章: https://mp.weixin.qq.com/s/O4DFpruMdQecJBPga6Q5zA ,我們從裏面選一個圓形的光斑。

實作圓形光斑的 shader :

#version 300 es
precision highp float;
in vec2 v_texCoord;
uniform sampler2D sTexture;
uniform sampler2D sPortraitTexture;
uniform highp vec2 inputSize;
layout(location = 0) out vec4 outColor;
floatsdCircle(in vec2 p, in float r){
return length(p)-r;
}
voidmain(){
float effectValue = 0.4;
vec2 uv = v_texCoord;
vec2 d = 1.0 / inputSize;
float scale = (inputSize.x + inputSize.y)/1080.0;
float kernel = floor(20.0 * scale * effectValue);
vec4 maxCol = vec4(0.0);
vec4 mask = texture(sPortraitTexture, uv);
for(float x = -kernel;x<kernel;x++) {
for(float y = -kernel;y<kernel;y++) {
vec2 xy = vec2(x,y)/kernel;
if(sdCircle(xy, 0.5 + effectValue * 0.2) < 0.0) {
vec4 col = texture(sTexture, uv + d * vec2(x,y));
maxCol = max(maxCol, col * mask.a);
}
}
}
outColor = maxCol;
}

有了光斑效果和摳出來的人像,可以使用 Blend 濾鏡來驗證一 波:

//Blend
#version 300 es
precision highp float;
in vec2 v_texCoord;
uniform sampler2D sTexture;//做完光斑效果後的影像
uniform sampler2D sPortraitTexture;//mask 分割出來的人像
layout(location = 0out vec4 outColor;
voidmain() {
vec4 portraitCol = texture(sPortraitTexture, v_texCoord);
vec4 srcCol = texture(sTexture, v_texCoord);
outColor = mix(srcCol, portraitCol, portraitCol.a);
}

效果如下:

「光斑」看起來很不自然,沒有虛化的感覺,接下來對影像先做模糊再做光斑的效果,看看融合起來效果如何。

模糊效果實作可以直接使用高斯模糊演算法,參考連結: https://mp.weixin.qq.com/s/D53C1KtY2slLBX28Ggmx2g ,這裏為了減少計算量選擇使用一個輕量的快速高斯模糊濾鏡,篇幅有限源碼不再貼出來,文末有獲取方式。

加了模糊之後的效果,看起來自然了很多:

細心觀察,發現了新的問題,人像邊緣有一圈黑色的邊,看起來很醜。

由2個原因導致的這個問題,一是演算法摳圖精確度不足,人像沒有扣幹凈;二是融合的時候沒有 alpha 預乘導致半透明的邊緣發黑。針對這兩個原因最佳化下我們的 blend shader :

#version 300 es
precision highp float;
in vec2 v_texCoord;
uniform sampler2D sTexture;//做完光斑和模糊效果後的影像
uniform sampler2D sPortraitTexture;/mask 分割出來的人像
layout(location = 0) out vec4 outColor;
voidmain(){
vec4 portraitCol = texture(sPortraitTexture, v_texCoord);
vec4 srcCol = texture(sTexture, v_texCoord);
srcCol *= srcCol.a;// alpha 預乘
portraitCol *= portraitCol.a;// alpha 預乘
outColor = mix(srcCol, portraitCol, portraitCol.a);
outColor.rgb /= max(0.000001, outColor.a);
}

來看最後的效果:

後續最佳化方向,使用景深(深度)圖控制不同區域光斑的大小和虛化的強度。需要完整程式碼的同學可以在下方掃碼添加我的微信: Byte-Flow 獲取。

-- END --

進技術交流群, 掃碼添加我的微信:Byte-Flow

獲取相關資料和源碼

學習音視訊、OpenGL ES、Vulkan 、Metal、影像濾鏡、視訊特效及相關渲染技術的付費社群,面試指導,1v1 簡歷服務,職業規劃。

我的付費社群

推薦: