OpenGL (一)OpenGL ES 繪制基礎(chǔ)
OpenGL (二)GLSurface 視頻錄制
OpenGL (三)濾鏡filter 應(yīng)用
OpenGL (四)貼紙和磨皮理論
OpenGL 圖像混合
在人臉的關(guān)鍵位置貼上如耳朵躬窜、鼻子等裝飾澄干,其實就是將裝飾疊加在原圖某個位置中。在此之前我們需要開啟混合模式度气!
// 開啟混合模式
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_CONSTANT_ALPHA);
混合就是把兩種顏色:源顏色和目標顏色混在一起。其中源是指現(xiàn)在要畫的貼紙招驴,目標則是已經(jīng)畫好的攝像頭圖像锁荔。
源因子和目標因子通過glBlendFunc函數(shù)設(shè)置,不同的組合方式很多泌辫。當前我們使用的組合為:
GL_ONE:表示使用源顏色的alpha值來作為因子;
GL_ONE_MINUS_SRC_ALPHA:表示用1.0減去源顏色的alpha值來作為因子九默;
裝飾品越透明震放,則混合的新顏色攝像頭圖像占比越重!
貼紙實現(xiàn)
實現(xiàn)貼紙只需要計算好貼紙在圖像中的坐標驼修,然后利用混合模式繪制到圖層中即可澜搅。可以在OpenGL中進行坐標處理邪锌,也可以直接在Java借助: GLES20.glViewport(x, y,width, height);
定位繪圖區(qū)域后再繪制貼紙紋理。
貼紙坐標
人臉定位與關(guān)鍵點定位得到的人臉與關(guān)鍵點坐標都是以送檢圖片左上角為起點癌瘾,基于圖片的寬與高觅丰。而顯示在屏幕上對應(yīng)的畫布寬高,與圖片寬高不一定一致妨退。定位繪圖區(qū)域需要根據(jù)畫布寬高進行定位妇萄。
也就是近大遠小的效果
時刻要注意OpenGL的世界坐標
- 做法,在java中做咬荷,相對畫布坐標(如:720x1180)
- opengl 里面著色器做冠句,需要按照世界坐標來,效率相對高
public class StickFilter extends AbstractFrameFilter {
private Bitmap bizi;
private int[] textures;
public StickFilter(Context context) {
super(context, R.raw.base_vert, R.raw.base_frag);
textures = new int[1];
OpenGLUtils.glGenTextures(textures);
// 把圖片加載到創(chuàng)建的紋理中
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
//....
bizi = BitmapFactory.decodeResource(context.getResources(), R.drawable.bizi);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D,0,bizi,0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
@Override
public int onDraw(int texture, FilterChain filterChain) {
return super.onDraw(texture, filterChain);
}
@Override
public void afterDraw(FilterContext filterContext) {
super.afterDraw(filterContext);
//畫鼻子
Face face = filterContext.face;
if (face == null) {
return;
}
//開啟混合模式
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
//計算坐標
//基于畫布的鼻子中心點的x
float x = face.nose_x / face.imgWidth * filterContext.width;
float y = (1.0f - face.nose_y / face.imgHeight) * filterContext.height;
//鼻子貼紙的寬與高
//通過左右嘴角的x的差作為鼻子裝飾品的寬
float mrx = face.mouseRight_x / face.imgWidth * filterContext.width;
float mlx = face.mouseLeft_x / face.imgWidth * filterContext.width;
int width = (int) (mrx - mlx);
//以嘴角的Y與鼻子中心點的y的差作為鼻子裝飾品的高
float mry = (1.0f - face.mouseRight_y / face.imgHeight) * filterContext.height;
int height = (int) (y - mry);
GLES20.glViewport((int) x - width / 2, (int) y - height / 2, width, height);
//畫鼻子
GLES20.glUseProgram(program);
vertexBuffer.position(0);
GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 0, vertexBuffer);
GLES20.glEnableVertexAttribArray(vPosition);
textureBuffer.position(0);
GLES20.glVertexAttribPointer(vCoord, 2, GLES20.GL_FLOAT, false, 0, textureBuffer);
GLES20.glEnableVertexAttribArray(vCoord);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
GLES20.glUniform1i(vTexture, 0);
//通知畫畫
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
//關(guān)閉混合模式
GLES20.glDisable(GLES20.GL_BLEND);
}
}
美顏磨皮
類似高斯模糊幸乒,這里就是平均模糊懦底,就是取周圍的點進行平均操作
美顏是個合成操作,看下幾個著色器:
- 模糊操作beauty_blur.frag
其實就是取水平和豎直方向進行平均計算
precision mediump float;
uniform sampler2D vTexture;
varying vec2 aCoord;
//紋理寬罕扎、高
uniform float texelWidthOffset;
uniform float texelHeightOffset;
vec4 blurCoord[5];
void main(){
//1聚唐、 進行模糊處理
// 偏移步距 (0,0.1)
vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);
blurCoord[0] = vec4(aCoord - singleStepOffset, aCoord + singleStepOffset);
blurCoord[1] = vec4(aCoord - 2.0 * singleStepOffset, aCoord + 2.0*singleStepOffset);
blurCoord[2] = vec4(aCoord - 3.0 * singleStepOffset, aCoord + 3.0*singleStepOffset);
blurCoord[3] = vec4(aCoord - 4.0 * singleStepOffset, aCoord + 4.0*singleStepOffset);
blurCoord[4] = vec4(aCoord - 5.0 * singleStepOffset, aCoord + 5.0*singleStepOffset);
// 計算當前坐標的顏色值
vec4 currentColor = texture2D(vTexture, aCoord);
vec3 sum = currentColor.rgb;
// 計算偏移坐標的顏色值總和
for (int i = 0; i < 5; i++) {
sum += texture2D(vTexture, blurCoord[i].xy).rgb;
sum += texture2D(vTexture, blurCoord[i].zw).rgb;
}
//平均值 模糊效果
vec4 blur = vec4(sum / 11.0, currentColor.a);
gl_FragColor = blur;
}
BeautyBlurFilter
public class BeautyblurFilter extends AbstractFrameFilter {
private int texelWidthOffset;
private int texelHeightOffset;
private float mTexelWidth;
private float mTexelHeight;
public BeautyblurFilter(Context context) {
super(context, R.raw.base_vert, R.raw.beauty_blur);
}
@Override
public void initGL(Context context, int vertexShaderId, int fragmentShaderId) {
super.initGL(context, vertexShaderId, fragmentShaderId);
texelWidthOffset = GLES20.glGetUniformLocation(program, "texelWidthOffset");
texelHeightOffset = GLES20.glGetUniformLocation(program, "texelHeightOffset");
}
@Override
public void beforeDraw(FilterContext filterContext) {
super.beforeDraw(filterContext);
GLES20.glUniform1f(texelWidthOffset, mTexelWidth);
GLES20.glUniform1f(texelHeightOffset, mTexelHeight);
}
/**
* 設(shè)置高斯模糊的寬高
*/
public void setTexelOffsetSize(float width, float height) {
mTexelWidth = width;
mTexelHeight = height;
if (mTexelWidth != 0) {
mTexelWidth = 1.0f / mTexelWidth;
} else {
mTexelWidth = 0;
}
if (mTexelHeight != 0) {
mTexelHeight = 1.0f / mTexelHeight;
} else {
mTexelHeight = 0;
}
}
}
- 高反差保留-邊緣銳化
beauty_highpass.frag
precision mediump float;
uniform sampler2D vTexture;
varying vec2 aCoord;
uniform sampler2D vBlurTexture;
void main(){
//2腔召、PS高反差保留- 邊緣銳化
vec4 currentColor = texture2D(vTexture, aCoord);
vec4 blurColor = texture2D(vBlurTexture, aCoord);
// 高反差 = 原圖 - 高斯模糊圖
vec4 highPassColor = currentColor - blurColor;
// clamp:獲得三個參數(shù)中大小處在中間的那個值
float intensity = 24.0;// 強光程度
//color = 2 * color1 * color2杆查;
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);
vec4 highPassBlur = vec4(highPassColor.rgb, 1.0);
gl_FragColor = highPassBlur;
}
BeautyHighpassFilter
public class BeautyHighpassFilter extends AbstractFrameFilter {
private int vBlurTexture;
private int blurTexture;
public BeautyHighpassFilter(Context context) {
super(context, R.raw.base_vert, R.raw.beauty_highpass);
}
@Override
public void initGL(Context context, int vertexShaderId, int fragmentShaderId) {
super.initGL(context, vertexShaderId, fragmentShaderId);
vBlurTexture = GLES20.glGetUniformLocation(program, "vBlurTexture");
}
@Override
public void beforeDraw(FilterContext filterContext) {
super.beforeDraw(filterContext);
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, blurTexture);
GLES20.glUniform1i(vBlurTexture, 1);
}
public void setBlurTexture(int blurTexture) {
this.blurTexture = blurTexture;
}
}
- 保邊預(yù)處理,整個面部的輪廓不至于處理掉
beauty_highpass_blur.frag
precision mediump float;
uniform sampler2D vTexture;//高反差保留紋理(高通濾波)
varying vec2 aCoord;
uniform int width;
uniform int height;
vec4 blurCoord[2];
void main(){
//3臀蛛、保邊預(yù)處理 保留邊沿的細節(jié)不被模糊掉
vec4 currentColor = texture2D(vTexture, aCoord);
vec2 singleStepOffset = vec2(width, height);
blurCoord[0] = vec4(aCoord - singleStepOffset, aCoord + singleStepOffset);
blurCoord[1] = vec4(aCoord - 2.0 *singleStepOffset, aCoord + 2.0*singleStepOffset);
vec3 sum = currentColor.rgb;
for (int i = 0; i < 2; i++) {
sum += texture2D(vTexture, blurCoord[i].xy).rgb;
sum += texture2D(vTexture, blurCoord[i].zw).rgb;
}
vec4 highPassBlur = vec4(sum*1.0/5.0, currentColor.a);
gl_FragColor = highPassBlur;
}
BeautyHighpassBlurFilter
public class BeautyHighpassBlurFilter extends AbstractFrameFilter {
private int widthIndex;
private int heightIndex;
public BeautyHighpassBlurFilter(Context context) {
super(context, R.raw.base_vert, R.raw.beauty_highpass_blur);
}
@Override
public void initGL(Context context, int vertexShaderId, int fragmentShaderId) {
super.initGL(context, vertexShaderId, fragmentShaderId);
widthIndex = GLES20.glGetUniformLocation(program, "width");
heightIndex = GLES20.glGetUniformLocation(program, "height");
}
@Override
public void beforeDraw(FilterContext filterContext) {
super.beforeDraw(filterContext);
GLES20.glUniform1i(widthIndex, filterContext.width);
GLES20.glUniform1i(heightIndex, filterContext.height);
}
}
- 磨皮
這不操作亲桦,就是混合顏色
beauty_adjust.frag
precision mediump float;
uniform sampler2D vTexture; //原圖
varying vec2 aCoord;
uniform sampler2D blurTexture; //原圖模糊
uniform sampler2D highpassBlurTexture; //模糊后的高反差圖
//磨皮程度 0-1.0
uniform float level;
void main(){
//4崖蜜、磨皮
vec4 currentColor = texture2D(vTexture, aCoord);
vec4 blurColor = texture2D(blurTexture, aCoord);
vec4 highpassBlurColor = texture2D(highpassBlurTexture, aCoord);
float value = clamp((min(currentColor.b, blurColor.b) - 0.2) * 5.0, 0.0, 1.0);
float maxChannelColor = max(max(highpassBlurColor.r, highpassBlurColor.g), highpassBlurColor.b);
float currentIntensity = (1.0 - maxChannelColor / (maxChannelColor + 0.2)) * value * level;
// 混合
vec3 resultColor = mix(currentColor.rgb, blurColor.rgb, currentIntensity);
gl_FragColor = vec4(resultColor, 1.0);
}
BeautyAdjustFilter
public class BeautyAdjustFilter extends AbstractFrameFilter {
private int level;
private int vBlurTexture;
private int vHighpassBlurTexture;
private int blurTexture;
private int highpassBlurTexture;
public BeautyAdjustFilter(Context context) {
super(context, R.raw.base_vert, R.raw.beauty_adjust);
}
@Override
public void initGL(Context context, int vertexShaderId, int fragmentShaderId) {
super.initGL(context, vertexShaderId, fragmentShaderId);
level = GLES20.glGetUniformLocation(program, "level");
vBlurTexture = GLES20.glGetUniformLocation(program, "blurTexture");
vHighpassBlurTexture = GLES20.glGetUniformLocation(program, "highpassBlurTexture");
}
@Override
public void beforeDraw(FilterContext filterContext) {
super.beforeDraw(filterContext);
GLES20.glUniform1f(level, filterContext.beautyLevel);
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, blurTexture);
GLES20.glUniform1i(vBlurTexture, 1);
GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, highpassBlurTexture);
GLES20.glUniform1i(vHighpassBlurTexture, 2);
}
public void setBlurTexture(int blurTexture) {
this.blurTexture = blurTexture;
}
public void setHighpassBlurTexture(int highpassBlurTexture) {
this.highpassBlurTexture = highpassBlurTexture;
}
}
注意:針對,一個著色器中客峭,處理多張圖片
傳參注意圖層豫领,
GL_TEXTURE1,GL_TEXTURE2......
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, blurTexture);
GLES20.glUniform1i(vBlurTexture, 1);
GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, highpassBlurTexture);
GLES20.glUniform1i(vHighpassBlurTexture, 2);
根據(jù)上面4個著色器,我們用一個路徑包含四個步驟進行處理
組合處理桃笙,自己不需要畫氏堤,責(zé)任鏈中調(diào)起下一個濾鏡
BeautyFilter.java
/**
* 組合, 自己不需要畫搏明,調(diào)起下一個濾鏡即可
*/
public class BeautyFilter extends AbstractFilter {
private BeautyblurFilter beautyVerticalblurFilter;
private BeautyblurFilter beautyHorizontalblurFilter;
private BeautyHighpassFilter beautyHighpassFilter;
private BeautyHighpassBlurFilter beautyHighpassBlurFilter;
private BeautyAdjustFilter beautyAdjustFilter;
public BeautyFilter(Context context) {
super(context, -1, -1);
beautyVerticalblurFilter = new BeautyblurFilter(context);
beautyHorizontalblurFilter = new BeautyblurFilter(context);
beautyHighpassFilter = new BeautyHighpassFilter(context);
beautyHighpassBlurFilter = new BeautyHighpassBlurFilter(context);
beautyAdjustFilter = new BeautyAdjustFilter(context);
}
@Override
public int onDraw(int texture, FilterChain filterChain) {
filterChain.setPause(true);
//1鼠锈、模糊處理
beautyVerticalblurFilter.setTexelOffsetSize(0, filterChain.filterContext.height);
int blurTexture = beautyVerticalblurFilter.onDraw(texture, filterChain);
beautyHorizontalblurFilter.setTexelOffsetSize(filterChain.filterContext.width,0);
blurTexture = beautyHorizontalblurFilter.onDraw(blurTexture,filterChain);
//2、高反差保留 邊緣銳化
beautyHighpassFilter.setBlurTexture(blurTexture);
int highpassTexture = beautyHighpassFilter.onDraw(texture, filterChain);
//3星著、保邊預(yù)處理 保留邊沿的細節(jié)不被模糊掉
int highpassBlurTexture = beautyHighpassBlurFilter.onDraw(highpassTexture, filterChain);
//4购笆、磨皮調(diào)整
beautyAdjustFilter.setBlurTexture(blurTexture);
beautyAdjustFilter.setHighpassBlurTexture(highpassBlurTexture);
int beautyTextre = beautyAdjustFilter.onDraw(texture, filterChain);
filterChain.setPause(false);
return filterChain.proceed(beautyTextre);
}
}