在前一篇文章Android OpenGLES 實(shí)時(shí)美顏(磨皮)的優(yōu)化,我們已經(jīng)介紹了關(guān)于實(shí)時(shí)美顏(磨皮)的一些優(yōu)化點(diǎn)。但在實(shí)際的優(yōu)化測試中發(fā)現(xiàn),當(dāng)處理器發(fā)熱之后,就無法保證預(yù)覽幀率了拌滋,主要還是高斯模糊處理的數(shù)據(jù)量比較大導(dǎo)致。因此猜谚,我們需要尋找新的磨皮方法败砂。
目前市面上關(guān)于磨皮方法有好多種赌渣,使用PS磨皮經(jīng)常用到的方法包括高反差保留、高低頻昌犹、中性灰以及雙線性等坚芜。其中中性灰和雙線性的效率一般,因此斜姥,我們從高反差保留鸿竖、高低頻這兩種方法中選擇。這里選擇使用高反差保留法做磨皮處理铸敏,PS中的高反差保留法進(jìn)行磨皮缚忧,隨手一搜便能找到很多文章,比如:
https://jingyan.baidu.com/article/455a99504d568fa1662778d6.html
接下來杈笔,我們嘗試著實(shí)現(xiàn)文章中講到的過程闪水。
第一步,對圖像做高斯模糊處理
關(guān)于高斯模糊的優(yōu)化蒙具,可以參考本人的文章:
OpenGLES濾鏡開發(fā)匯總 —— 高斯模糊實(shí)現(xiàn)以及優(yōu)化
對于人像進(jìn)行高斯模糊球榆,我們設(shè)計(jì)一個(gè)11x11的高斯算子對圖像進(jìn)行高斯模糊,shader如下:
vertex shader :
uniform mat4 uMVPMatrix;
attribute vec4 aPosition;
attribute vec4 aTextureCoord;
// 高斯算子左右偏移值禁筏,當(dāng)偏移值為5時(shí)持钉,高斯算子為 11 x 11
const int SHIFT_SIZE = 5;
uniform highp float texelWidthOffset;
uniform highp float texelHeightOffset;
varying vec2 textureCoordinate;
varying vec4 blurShiftCoordinates[SHIFT_SIZE];
void main() {
gl_Position = uMVPMatrix * aPosition;
textureCoordinate = aTextureCoord.xy;
// 偏移步距
vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);
// 記錄偏移坐標(biāo)
for (int i = 0; i < SHIFT_SIZE; i++) {
blurShiftCoordinates[i] = vec4(textureCoordinate.xy - float(i + 1) * singleStepOffset,
textureCoordinate.xy + float(i + 1) * singleStepOffset);
}
}
fragment shader:
precision mediump float;
varying vec2 textureCoordinate;
uniform sampler2D inputTexture;
const int SHIFT_SIZE = 5; // 高斯算子左右偏移值
varying vec4 blurShiftCoordinates[SHIFT_SIZE];
void main() {
// 計(jì)算當(dāng)前坐標(biāo)的顏色值
vec4 currentColor = texture2D(inputTexture, textureCoordinate);
mediump vec3 sum = currentColor.rgb;
// 計(jì)算偏移坐標(biāo)的顏色值總和
for (int i = 0; i < SHIFT_SIZE; i++) {
sum += texture2D(inputTexture, blurShiftCoordinates[i].xy).rgb;
sum += texture2D(inputTexture, blurShiftCoordinates[i].zw).rgb;
}
// 求出平均值
gl_FragColor = vec4(sum * 1.0 / float(2 * SHIFT_SIZE + 1), currentColor.a);
}
經(jīng)過以上的shader進(jìn)行高斯模糊處理之后,我們得到這樣一張高斯模糊圖像:
第二步篱昔,利用高通濾波器做高反差保留
在PS的高反差保留磨皮方法中每强,高反差保留磨皮混合采用的是強(qiáng)光模式,計(jì)算公式為:color = 2 * color1 * color2旱爆。因此舀射,我們設(shè)計(jì)出這樣一個(gè)高通濾波器窘茁,其shader如下:
fragment shader:
precision mediump float;
varying vec2 textureCoordinate;
uniform sampler2D inputTexture; // 輸入原圖
uniform sampler2D blurTexture; // 高斯模糊圖片
const float intensity = 24.0; // 強(qiáng)光程度
void main() {
lowp vec4 sourceColor = texture2D(inputTexture, textureCoordinate);
lowp vec4 blurColor = texture2D(blurTexture, textureCoordinate);
// 高通濾波之后的顏色值
highp vec4 highPassColor = sourceColor - blurColor;
// 對應(yīng)混合模式中的強(qiáng)光模式(color = 2.0 * color1 * color2)怀伦,對于高反差的顏色來說,color1 和color2 是同一個(gè)
highPassColor.r = clamp(2.0 * highPassColor.r * highPassColor.r * intensity, 0.0, 1.0);
highPassColor.g = clamp(2.0 * highPassColor.g * highPassColor.g * intensity, 0.0, 1.0);
highPassColor.b = clamp(2.0 * highPassColor.b * highPassColor.b * intensity, 0.0, 1.0);
// 輸出的是把痘印等過濾掉
gl_FragColor = vec4(highPassColor.rgb, 1.0);
}
經(jīng)過高通濾波器之后山林,我們得到這樣一個(gè)紋理圖像:
可以看到房待,經(jīng)過三通道強(qiáng)光混合處理后,痘印驼抹、邊沿等地方都清晰起來了桑孩。強(qiáng)光的程度,一般是3的倍數(shù)框冀,這里取24倍流椒。
第三步,保邊預(yù)處理
到這一步明也,其實(shí)我們已經(jīng)得到了需要過濾顏色值宣虾,但在這一張圖中惯裕,也把邊沿的顏色差值包含進(jìn)來了。我們接下來需要過濾掉邊沿的顏色差值绣硝。這樣在后續(xù)的處理中蜻势,我們可以保留邊沿的細(xì)節(jié)不被模糊掉。因此接下來鹉胖,我們需要將經(jīng)過高通濾波得到的紋理握玛,再做一次高斯模糊。不過這一次不能11 x11 這么大的高斯算子甫菠,我們選擇一個(gè) 5 x 5 大小的高斯算子挠铲。高斯模糊的shader 如下:
vertex shader:
uniform mat4 uMVPMatrix;
attribute vec4 aPosition;
attribute vec4 aTextureCoord;
// 高斯算子左右偏移值,當(dāng)偏移值為2時(shí)淑蔚,高斯算子為5 x 5
const int SHIFT_SIZE = 2;
uniform highp float texelWidthOffset;
uniform highp float texelHeightOffset;
varying vec2 textureCoordinate;
varying vec4 blurShiftCoordinates[SHIFT_SIZE];
void main() {
gl_Position = uMVPMatrix * aPosition;
textureCoordinate = aTextureCoord.xy;
// 偏移步距
vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);
// 記錄偏移坐標(biāo)
for (int i = 0; i < SHIFT_SIZE; i++) {
blurShiftCoordinates[i] = vec4(textureCoordinate.xy - float(i + 1) * singleStepOffset,
textureCoordinate.xy + float(i + 1) * singleStepOffset);
}
}
fragment shader:
precision mediump float;
varying vec2 textureCoordinate;
uniform sampler2D inputTexture;
// 高斯算子左右偏移值市殷,當(dāng)偏移值為2時(shí),高斯算子為5 x 5
const int SHIFT_SIZE = 2;
varying vec4 blurShiftCoordinates[SHIFT_SIZE];
void main() {
// 計(jì)算當(dāng)前坐標(biāo)的顏色值
vec4 currentColor = texture2D(inputTexture, textureCoordinate);
mediump vec3 sum = currentColor.rgb;
// 計(jì)算偏移坐標(biāo)的顏色值總和
for (int i = 0; i < SHIFT_SIZE; i++) {
sum += texture2D(inputTexture, blurShiftCoordinates[i].xy).rgb;
sum += texture2D(inputTexture, blurShiftCoordinates[i].zw).rgb;
}
// 求出平均值
gl_FragColor = vec4(sum * 1.0 / float(2 * SHIFT_SIZE + 1), currentColor.a);
}
將高通濾波器得到的紋理刹衫,經(jīng)過高斯模糊處理后醋寝,得到這樣一張紋理:
對比高通濾波器處理后的紋理,邊沿細(xì)節(jié)變得模糊了带迟,而且音羞,需要過濾的顏色差值仍舊保留著。到這一步仓犬,我們就得到了做磨皮處理的前置紋理嗅绰。接下來就是高反差保留磨皮的最后也是最重要的一步。
第四步搀继,磨皮調(diào)節(jié)
經(jīng)過前面的處理窘面,我們得到一張輸入圖片的高斯模糊紋理,以及一張高反差保留的高斯模糊紋理叽躯。我們使用這兩張紋理财边,通過比較藍(lán)色通道,計(jì)算出需要磨皮的實(shí)際強(qiáng)度值点骑,與原圖進(jìn)行混合處理酣难,然后輸出最終的紋理。shader如下所示:
precision mediump float;
varying vec2 textureCoordinate;
uniform sampler2D inputTexture; // 輸入原圖
uniform sampler2D blurTexture; // 原圖的高斯模糊紋理
uniform sampler2D highPassBlurTexture; // 高反差保留的高斯模糊紋理
uniform lowp float intensity; // 磨皮程度
void main() {
lowp vec4 sourceColor = texture2D(inputTexture, textureCoordinate);
lowp vec4 blurColor = texture2D(blurTexture, textureCoordinate);
lowp vec4 highPassBlurColor = texture2D(highPassBlurTexture, textureCoordinate);
// 調(diào)節(jié)藍(lán)色通道值
mediump float value = clamp((min(sourceColor.b, blurColor.b) - 0.2) * 5.0, 0.0, 1.0);
// 找到模糊之后RGB通道的最大值
mediump float maxChannelColor = max(max(highPassBlurColor.r, highPassBlurColor.g), highPassBlurColor.b);
// 計(jì)算當(dāng)前的強(qiáng)度
mediump float currentIntensity = (1.0 - maxChannelColor / (maxChannelColor + 0.2)) * value * intensity;
// 混合輸出結(jié)果
lowp vec3 resultColor = mix(sourceColor.rgb, blurColor.rgb, currentIntensity);
// 輸出顏色
gl_FragColor = vec4(resultColor, 1.0);
}
經(jīng)過上面的處理之后黑滴,我們就得到磨皮處理的結(jié)果如下:
可以看到憨募,經(jīng)過高反差保留磨皮后的結(jié)果,磨皮效果還不錯(cuò)袁辈,而且720P磨皮處理時(shí)菜谣,在高通驍龍625處理器上,經(jīng)過高反差保留磨皮之后,預(yù)覽幀率能夠保持在30FPS左右尾膊。我們可以看到甘磨,邊沿細(xì)節(jié)還是不夠明顯,所以眯停,我們可以使用USM銳化增強(qiáng)邊沿細(xì)節(jié)部分济舆。這篇文章就不講解USM銳化的實(shí)現(xiàn)了。
詳細(xì)實(shí)現(xiàn)過程莺债,可以參考本人的開源相機(jī)項(xiàng)目:
CainCamera
CainCamera的FilterLibrary中有經(jīng)過優(yōu)化后的實(shí)時(shí)美顏(磨皮)實(shí)現(xiàn)滋觉。