Android opengl 實現(xiàn)動態(tài)貼紙(仿QQ的拍攝)

回顧

在前面視頻錄制篇尾(http://m.itdecent.cn/p/4f63b2436401)中有提到視頻錄制+人臉識別+貼紙的想法,而上一篇(http://m.itdecent.cn/p/6d2d4f00b43c)中也使用opengl實現(xiàn)了多種特效,在這些技術(shù)與理論基礎(chǔ)上能否實現(xiàn)市面上常見的動態(tài)貼紙效果呢?

效果圖

先來個效果圖,這個是抓取QQ的資源包來做的:


視頻動態(tài)貼紙?zhí)匦?gif

分析

素材解析

剛開始看到QQ的這個拍攝特效時,覺得很有意思,又正好之前就想著研究一下動態(tài)貼紙的做法,于是在sd卡中找到QQ的文件目錄.../tencent/MobileQQ/capture_ptv_template/ptv_template_usable/video_qiuhongbaowu3_iOS/,里面有bgm、hongbao兩個目錄和params.json文件,bgm中是背景音樂mp3文件,hongbao中是80張素材圖片,params.json是規(guī)則配置文件。

至此大體能知道,其實視頻動效是由一系列圖片連續(xù)顯示的效果。查看params.json,里面包含類型定義、顯示時間間隔、素材列表及顯示順序,同時每個素材信息還包括鏤空中心點、鏤空區(qū)域大小、旋轉(zhuǎn)角度等信息。顯然這個鏤空區(qū)域是用來填充人臉的,這樣人臉圖像放在底層,貼紙在上層,需要讓鏤空區(qū)域與人臉重合,以便看起來就像是人臉在貼紙上的效果。

原理與關(guān)鍵點分析

解析素材后可以看出,根據(jù)配置文件,對素材列表根據(jù)順序及時間間隔,不斷替換顯示出來,其實視頻也是這個原理?,F(xiàn)在有個問題:怎么讓素材圖片隔段時間替換一張,不斷顯示出來呢?

前面文章中的視頻錄制,就是需要設(shè)置幀率,也就是說每隔一段時間都會刷新畫面,如果我們設(shè)置filter,那么每個filter的draw就會在刷新時被調(diào)用。所以我們寫一個filter,解析配置文件得到文件列表及其他信息,這樣在draw函數(shù)中,每次根據(jù)時間來判斷當前應該畫哪一張圖:

...
if (this.loopStartTime == -1L) {
    this.loopStartTime = System.currentTimeMillis();
}
int currentIndex = (int) ((System.currentTimeMillis() - this.loopStartTime) / this.mSticker.frameDuration);
if (currentIndex >= this.mSticker.frameCount) {
    if (!this.mSticker.looping) {
        this.loopStartTime = -1L;
        this.lastIndex = -1;
        return;
    }
    currentIndex = 0;//新一輪中從第一張開始顯示,并重新計時
    this.loopStartTime = System.currentTimeMillis();
}
if (currentIndex < 0) {
    currentIndex = 0;
}
StaticImage image = mSticker.images.get(currentIndex);
if (this.lastIndex != currentIndex) {
    mTexture.load(image.path);//加載圖片
}
...

對于渲染圖片,在之前的demo中已經(jīng)有現(xiàn)成源碼可以使用,那如何把頭像繪制到貼紙的鏤空位置呢?

頭像摳圖

人臉識別可以借助第三方的sdk,face++、商湯等收費的,識別效果比較好,主要是識別到的關(guān)鍵點比較多,或者訊飛的免費,基本能夠滿足。
根據(jù)識別結(jié)果的頭像區(qū)域,再計算貼紙鏤空區(qū)域位置與大小,使用glsl做渲染位置計算:

precision highp float;
varying highp vec2 vCamTextureCoord;
uniform sampler2D uCamTexture;
uniform vec4 srcRect;//頭像在源畫面(畫布)中的位置
uniform vec4 dstRect;//頭像在新畫布中的位置

void main(){
    lowp vec4 c1 = texture2D(uCamTexture, vCamTextureCoord);

    //注意這個y坐標的計算,因為屏幕坐標第的Y軸方向與紋理的Y軸方向是相反的
    lowp vec2 vCamTextureCoord2 = vec2(vCamTextureCoord.x,1.0 - vCamTextureCoord.y);
    vec2 point = vCamTextureCoord2;

    if(point.x>dstRect.r && point.x<dstRect.b && point.y>dstRect.g && point.y<dstRect.a)
    {
        //這個需要根據(jù)縮放比例來計算真實位置
        vec2 imagexy = vec2(
            (srcRect.b - srcRect.r) * (point.x - dstRect.r) / (dstRect.b - dstRect.r) + srcRect.r,
            1.0 - ((srcRect.a - srcRect.g) * (point.y - dstRect.g) / (dstRect.a - dstRect.g) + srcRect.g)
        );
        c1 = texture2D(uCamTexture, imagexy);
    }

    gl_FragColor = c1;
}

至此,我們可以這樣理解這個過程:
原始畫面 -> 摳頭像 -> 畫貼紙
即兩次渲染,得到合成的畫面,再加上時間間隔及不同貼紙的顯示,就得到上術(shù)的效果。
由于我們在摳頭像后是繪制到當前貼紙的鏤空位置,這樣無論頭像在原始畫面的什么位置,只要能識別出來,都不會影響摳圖操作及最終效果。

總結(jié)

現(xiàn)在來看,動態(tài)貼紙從原理上其實也是很簡單的,而且組合方式很多,所以才能看到很多APP上有那么多的貼紙模版,當然有些是有加入肢體識別的,比如QQ,這方便鵝廠坐的很好。
其實也要“感謝”那些第三方SDK的收費,還有XXX的UOK,促使我們自己來研究這些。

吐槽一下,對于那些本來沒有基礎(chǔ)、不想花錢,而還想要達到別人的精致效果的,那就只能呵呵了。

本文相關(guān)的代碼暫時不會更新到DEMO中,其實思想比較重要。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,704評論 4 61
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,355評論 25 708
  • 與哥大風格完全不同,紐約大學沒有獨立封閉的、圍墻般的校園,因為它的校園與整個紐約市融為一體,也可以說紐約大學是以整...
    午后窗臺的貓閱讀 478評論 0 0
  • 1.現(xiàn)在的你在哪,在干什么? 2.四年后的你在哪,和誰在一起? 3.如果你遇見十年后的你,會對自己說些什么? ——...
    懶羊羊?qū)W長閱讀 426評論 0 3
  • 誰的青春不迷茫,沒有看過這本書,甚至也沒有看過劉同的其他書,不過我身邊的很多朋友倒是蠻喜歡劉同的。那為什么會去看這...
    半__夏_閱讀 264評論 0 0

友情鏈接更多精彩內(nèi)容