深度學習AI美顏系列——人像靜態/動態貼紙特效演算法實現 | CSDN博文精選
作者 | Trent 1985
來源 | CSDN部落格
人像靜態/動態貼紙特效幾乎已經是所有影像影片處理類/直播類app的必需品了,這個功能看起來複雜,實際上很簡單,本文將給大家做個詳細的講解。
我們先來看一下FaceU的兩張效果圖:
這兩張效果圖中, 我們可以看到一些可愛的聖誕帽貼紙和小鹿形象貼紙,在人臉區域,自動貼上不同的貼紙,便會生成很多有趣的特效,這也是大家愛不釋手的原因。
我們從靜態貼紙講起,動態貼紙實際上是靜態貼紙的迴圈播放而已。人像靜態貼紙的鼻祖是in app,而後FaceU的動態貼紙風靡一時,現在靜態/動態貼紙已經隨處可見了。它的演算法分為兩個部分:
1. 人臉檢測與關鍵點識別
所謂人像貼紙,第一步必然是人臉檢測與關鍵點識別。在有人臉的情況下獲取到必須的關鍵點,這樣我們才能準確的打上貼紙。現有的app中,比如FaceU和輕顏相機,當然他們是一家的,他們的貼紙基本上都是數十個人臉特徵點的模板配置,也就是說,完成一個貼紙效果,需要數十個點位資訊的支援。不過,個人認為,最簡單的貼紙,可以從三個點開始。
關於人臉特徵點識別演算法,相關的論文或者開源的程式碼工程,已經多如牛毛,這裡我不 一一例舉,本人這裡以mtcnn為例,因為本文演算法只需要三個點即可。mtcnn在檢測到人臉的情況下,會給我們提供5個特徵點,如下圖所示:
mtcnn的演算法詳解大家也可以在網路資源中自行學習,這裡我們僅僅給出網路結構示意圖,如圖所示。
對於上述三個網路,分別為三個階段:
Stage 1:使用P-Net是一個全卷積網路,用來生成候選窗和邊框迴歸向量(bounding box regression vectors)。使用Bounding box regression的方法來校正這些候選窗,使用非極大值抑制(NMS)合併重疊的候選框;
Stage 2:使用N-Net改善候選窗,將透過P-Net的候選窗輸入R-Net中,拒絕掉大部分false的視窗,繼續使用Bounding box regression和NMS合併;
Stage 3:使用O-Net輸出最終的人臉框和特徵點位置。和第二步類似,但是不同的是生成5個特徵點位置;
本文使用NCNN呼叫MTCNN的模型,上層採用C#呼叫,程式碼如下:
/*************************************************Copyright: Copyright Trent.Author: TrentDate: 2015-03-09Description:MTCNN.**************************************************/#include"Trent.h"#include "mtcnn.h"#include <stdint.h>static char* model_path = "C:/Users/Administrator/Desktop/mtcnn/001_TestDemo/TestDemo/TestDemo_C/models";MTCNN* mtcnn;int FD_Initialize{mtcnn = new MTCNN(model_path);return 0;};int FD_Process(unsigned char* srcData, int width, int height, int stride, int faceInfos[15]){unsigned char* data = (unsigned char*)malloc(sizeof(unsigned char) * height * width * 3);unsigned char* pSrc = srcData;unsigned char* pData = data;for (int j = 0; j < height; j++){for (int i = 0; i < width; i++){pData[0] = pSrc[0];pData[1] = pSrc[1];pData[2] = pSrc[2];pData += 3;pSrc += 4;}}ncnn::Mat ncnn_img = ncnn::Mat::from_pixels(data, ncnn::Mat::PIXEL_RGB, width, height);std::vector<Bbox> finalBbox;mtcnn->detect(ncnn_img, finalBbox);if(finalBbox.size > 0){faceInfos[0] = 1;faceInfos[1] = finalBbox[0].x1;faceInfos[2] = finalBbox[0].y1;faceInfos[3] = finalBbox[0].x2 - finalBbox[0].x1;faceInfos[4] = finalBbox[0].y2 - finalBbox[0].y1;faceInfos[5] = finalBbox[0].ppoint[0];faceInfos[6] = finalBbox[0].ppoint[5];faceInfos[7] = finalBbox[0].ppoint[1];faceInfos[8] = finalBbox[0].ppoint[6];faceInfos[9] = finalBbox[0].ppoint[2];faceInfos[10] = finalBbox[0].ppoint[7];faceInfos[11] = finalBbox[0].ppoint[3];faceInfos[12] = finalBbox[0].ppoint[8];faceInfos[13] = finalBbox[0].ppoint[4];faceInfos[14] = finalBbox[0].ppoint[9];}free(data);return 0;};void FD_Unitialize{if(model_path != )free(model_path);delete(mtcnn);}
拿到五個人臉特徵點,這一步我們就完成了。
2. 貼紙融合
有了特徵點,我們如何將貼紙融合到恰當的位置?
①計算基準點
我們從5個特徵點中,計算得到三個關鍵點A,B,C;
-
A為左眼中心點
-
B為右眼中心點
-
C為嘴巴水平中心點
對於一張人臉,它的這三個點變化比較小,同時又可以覆蓋整個臉部區域,因此,具有整張臉的代表性。
②構建模特基準點
選取一張模特圖,要求五官端正,比例協調,如下圖所示:
在圖中標定出三個人臉關鍵點位置A,B,C,如藍色點所示,並記錄位置資訊:
③構建貼紙模板
我們使用如下兩個貼紙,在PS中將貼紙放置到模特臉上合適的位置,然後儲存兩個貼紙模板為mask_a,mask_b,這樣兩個貼紙模板就製作完成了,模板如下圖所示:
③貼圖
對於任意一張使用者照片,先使用MTCNN得到人臉5個關鍵點,計算出人臉關鍵點A,B,C,我們記做A0,B0,C0;
然後,使用仿射變換,公式如下圖所示所示,將A0,B0,C0對映到模特圖A,B,C三點,得到仿射變換矩陣H;
仿射變換H矩陣求解程式碼如下:
void GetTexTransMatrix(float x1, float y1, float x2, float y2, float x3, float y3,float tx1, float ty1, float tx2, float ty2, float tx3, float ty3, float*texMatrix){float detA;detA = tx1*ty2 + tx2*ty3 + tx3*ty1 - tx3*ty2 - tx1*ty3 - tx2*ty1;float A11, A12, A13, A21, A22, A23, A31, A32, A33;A11 = ty2 - ty3;A21 = -(ty1 - ty3);A31 = ty1 - ty2;A12 = -(tx2 - tx3);A22 = tx1 - tx3;A32 = -(tx1 - tx2);A13 = tx2*ty3 - tx3*ty2;A23 = -(tx1*ty3 - tx3*ty1);A33 = tx1*ty2 - tx2*ty1;texMatrix[0] = (x1*A11 + x2*A21 + x3*A31) / detA;texMatrix[1] = (x1*A12 + x2*A22 + x3*A32) / detA;texMatrix[2] = (x1*A13 + x2*A23 + x3*A33) / detA;texMatrix[3] = (y1*A11 + y2*A21 + y3*A31) / detA;texMatrix[4] = (y1*A12 + y2*A22 + y3*A32) / detA;texMatrix[5] = (y1*A13 + y2*A23 + y3*A33) / detA;
最後,根據H遍歷使用者照片,將使用者照片畫素對映到貼紙模板mask_a或者mask_b中,根據貼紙模板畫素的透明度進行alpha混合,即可得到最終效果,如下圖所示:
最後給出完整的自動人像貼紙程式碼,僅僅50行左右,即可實現靜態圖貼紙效果,如果大家需要動態貼紙,可以迴圈播放不同貼紙即可,程式碼如下:
#include"Trent_Sticker.h"#include"Trent.h"void GetTexTransMatrix(float x1, float y1, float x2, float y2, float x3, float y3,float tx1, float ty1, float tx2, float ty2, float tx3, float ty3, float*texMatrix){float detA;detA = tx1*ty2 + tx2*ty3 + tx3*ty1 - tx3*ty2 - tx1*ty3 - tx2*ty1;float A11, A12, A13, A21, A22, A23, A31, A32, A33;A11 = ty2 - ty3;A21 = -(ty1 - ty3);A31 = ty1 - ty2;A12 = -(tx2 - tx3);A22 = tx1 - tx3;A32 = -(tx1 - tx2);A13 = tx2*ty3 - tx3*ty2;A23 = -(tx1*ty3 - tx3*ty1);A33 = tx1*ty2 - tx2*ty1;texMatrix[0] = (x1*A11 + x2*A21 + x3*A31) / detA;texMatrix[1] = (x1*A12 + x2*A22 + x3*A32) / detA;texMatrix[2] = (x1*A13 + x2*A23 + x3*A33) / detA;texMatrix[3] = (y1*A11 + y2*A21 + y3*A31) / detA;texMatrix[4] = (y1*A12 + y2*A22 + y3*A32) / detA;texMatrix[5] = (y1*A13 + y2*A23 + y3*A33) / detA;}int Trent_Sticker(unsigned char* srcData, int width, int height, int stride, unsigned char* mask, int maskWidth, int maskHeight, int maskStride, int srcFacePoints[6], int maskFacePoints[6], int ratio){int ret = 0;float H[6];GetTexTransMatrix(maskFacePoints[0], maskFacePoints[1], maskFacePoints[2], maskFacePoints[3], maskFacePoints[4], maskFacePoints[5], srcFacePoints[0], srcFacePoints[1], srcFacePoints[2], srcFacePoints[3], srcFacePoints[4], srcFacePoints[5], H);for (int j = 0; j < height; j++){for (int i = 0; i < width; i++){float x = (float)i;float y = (float)j;float tx = 0;float ty = 0;tx = (int)((H[0] * (x)+H[1] * (y)+H[2]) + 0.5);ty = (int)((H[3] * (x)+H[4] * (y)+H[5]) + 0.5);tx = CLIP3(tx, 0, maskWidth - 1);ty = CLIP3(ty, 0, maskHeight - 1);int mb = mask[(int)tx * 4 + (int)ty * maskStride];int mg = mask[(int)tx * 4 + (int)ty * maskStride + 1];int mr = mask[(int)tx * 4 + (int)ty * maskStride + 2];int alpha = mask[(int)tx * 4 + (int)ty * maskStride + 3];int b = srcData[i * 4 + j * stride];int g = srcData[i * 4 + j * stride + 1];int r = srcData[i * 4 + j * stride + 2];srcData[(int)i * 4 + (int)j * stride] = CLIP3((b * (255 - alpha) + mb * alpha) / 255, 0, 255);srcData[(int)i * 4 + (int)j * stride + 1] = CLIP3((g * (255 - alpha) + mg * alpha) / 255, 0, 255);srcData[(int)i * 4 + (int)j * stride + 2] = CLIP3((r * (255 - alpha) + mr * alpha) / 255, 0, 255);}}return ret;};
最後,給出完整工程的原始碼,包含MTCNN呼叫程式碼和靜態貼紙程式碼: 1985/10891121
注意,程式碼執行環境為VS2015,執行時需要把model_path修改為自己的本地路徑即可。
DEMO原始碼介面如下:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69946223/viewspace-2663426/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 深度學習AI美顏系列----基於摳圖的人像特效演算法深度學習AI特效演算法
- 美顏sdk動態貼紙的實現流程
- 直播美顏SDK動態貼紙詳解
- 美顏sdk動態貼紙是什麼?
- 美顏sdk動態貼紙開發技術詳解
- AI與科技加持的美顏sdk動態貼紙是什麼樣的?AI
- 直播中常用的美顏sdk動態貼紙功能是什麼?技術怎麼實現的?
- 美顏sdk的美白、貼紙、磨皮功能的實現流程
- 美顏SDK動態貼紙在短影片中爆火的原因是什麼?包含哪些演算法?演算法
- 直播美顏SDK的美顏特效涉及哪些美顏演算法?特效演算法
- 影片美顏SDK動態處理技術與靜態處理技術
- 美顏sdk的動態面具、3D面具實現流程3D
- 美顏特效SDK的工作原理,原相機如何實現影片實時美顏特效
- JAVA學習篇--靜態代理VS動態代理Java
- 靜態照片一鍵動態化,教你如何整合人像復活能力
- Laravel 如何實現既能靜態呼叫,又能動態呼叫Laravel
- Linux靜態庫和動態庫學習總結Linux
- 美顏SDK一鍵美顏的演算法實現流程演算法
- AR動態貼紙SDK,讓創作更加生動有趣
- 美顏SDK人臉貼紙已成直播平臺剛需
- Java基礎系列-靜態代理和動態代理Java
- Java設計模式學習06——靜態代理與動態代理Java設計模式
- Delphi系列談之:Delphi中的靜態屬性及靜態方法的實現 (轉)
- 美攝AR人像美顏,全新視覺體驗視覺
- 美顏SDK人像摳圖技術是什麼?人像摳圖技術是如何實現的?
- 偽靜態、靜態和動態的區別
- Linux 依賴動態庫 / 靜態庫的動態態庫 / 靜態庫Linux
- 藉助 Webpack 靜態分析能力實現程式碼動態載入Web
- C#靜態建構函式及靜態變數學習C#函式變數
- 【騰訊深度學習系列】深度學習及並行化實現概述深度學習並行
- 創業,你選擇靜態還是動態語言?創業
- AI美顏SDK演算法詳解AI演算法
- Rougamo、Fody 實現靜態AopGAM
- canvas實現動態替換人物的背景顏色Canvas
- .Net Core Razor動態選單實現
- Object C學習筆記10-靜態方法和靜態屬性Object筆記
- 靜態代理和動態代理
- 靜態路由和動態路由路由