OpenGL(四)貼紙和磨皮理論

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的世界坐標

  1. 做法,在java中做咬荷,相對畫布坐標(如:720x1180)
  2. 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);

    }
}

美顏磨皮

類似高斯模糊幸乒,這里就是平均模糊懦底,就是取周圍的點進行平均操作

美顏是個合成操作,看下幾個著色器:

  1. 模糊操作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;
        }
    }
}
  1. 高反差保留-邊緣銳化
    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;
    }
}

  1. 保邊預(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);
    }

}
  1. 磨皮
    這不操作亲桦,就是混合顏色
    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);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者虚循。
  • 序言:七十年代末同欠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子横缔,更是在濱河造成了極大的恐慌铺遂,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茎刚,死亡現(xiàn)場離奇詭異襟锐,居然都是意外死亡,警方通過查閱死者的電腦和手機膛锭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門粮坞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人初狰,你說我怎么就攤上這事莫杈。” “怎么了奢入?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵筝闹,是天一觀的道長。 經(jīng)常有香客問我腥光,道長丁存,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任柴我,我火速辦了婚禮解寝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘艘儒。我一直安慰自己聋伦,他們只是感情好夫偶,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著觉增,像睡著了一般兵拢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逾礁,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天说铃,我揣著相機與錄音,去河邊找鬼嘹履。 笑死腻扇,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的砾嫉。 我是一名探鬼主播幼苛,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼焕刮!你這毒婦竟也來了舶沿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤配并,失蹤者是張志新(化名)和其女友劉穎括荡,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體溉旋,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡一汽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了低滩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡岩喷,死狀恐怖恕沫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情纱意,我是刑警寧澤婶溯,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站偷霉,受9級特大地震影響迄委,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜类少,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一叙身、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧硫狞,春花似錦信轿、人聲如沸晃痴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽倘核。三九已至,卻和暖如春即彪,著一層夾襖步出監(jiān)牢的瞬間紧唱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工隶校, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留漏益,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓惠况,卻偏偏與公主長得像遭庶,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子稠屠,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容