深度學習AI美顏系列---SpecialFace特效濾鏡
阿新 • • 發佈:2018-12-22
SpecialFace濾鏡這個名字實際上是本人自己起的,因為這個濾鏡是一種比較另類的,人臉美化特效,所以給了這個名字。先看一下效果:
下面我們來分析一下這個濾鏡效果的演算法邏輯:
1,設計一張逼真的模版A,如下圖所示:
這張模版中有透明區域和白色區域以及花草區域三個區域組成;
同時,在A中標定出兩個人眼眼睛的位置E1和E2。
2,對人物照片原圖S進行人臉檢測與特徵點識別;
這裡我們只需要兩個眼睛的點位SE1和SE2即可,可以直接使用騰訊或者Face++開源人臉特徵點檢測。
這個開原始碼是68個點的,沒有眼睛中心點,但是可以根據眼睛輪廓點計算出眼睛中心點。
3,根據人眼特徵點位置,將A放置到S的人臉區域,A和S中的人眼位置對齊;
這一步演算法是仿射變換,將E1和E2對齊到SE1和SE2上,這樣A即對齊到了S中。
仿射變換公式如下:
在OPENCV中有API介面呼叫:
void cv::warpAffine ( InputArray src, OutputArray dst, InputArray M, Size dsize, int flags = INTER_LINEAR, int borderMode = BORDER_CONSTANT, const Scalar & borderValue = Scalar() )
4,對A的三個區域分別處理:
①A中透明區域顯示S中的內容;
②A中花草區域填充到S中對應位置;
③A中的白色區域顯示對應S中的人臉區域,並對該區域畫素進行右向的平移,平移距離OFFSET的大小,反映了影象中人臉裂開的程度;
以①-③的步驟將A和S進行融合,即得到該濾鏡的效果圖。
用類似的方法可以做出很多另類的濾鏡風格,濾鏡的真實度與模版的逼真度相關,所以模版的設計要求比較高。
本演算法核心程式碼如下:
int TriangleProcess(Triangle srcTriangle, unsigned char* srcData, int width ,int height, int stride, Triangle dstTriangle, unsigned char* dstData, int dWidth, int dHeight, int dStride, unsigned char* pFaceLabel, int ratio) { unsigned char* tempData = (unsigned char*)malloc(sizeof(unsigned char) * height * stride); memcpy(tempData, srcData, sizeof(unsigned char) * height * stride); int i, j, pos, mw, mh, nx, ny, r, g, b, a, k, nk, temp; float px = 0, py = 0, cx, cy, AR, AG, AB, AA; int x0, y0, index_x0y0, index_x0y1, index_x1y0, index_x1y1; float disX, disY, tmp1, tmp2; TPoint curPoint; TPoint A =srcTriangle.A; TPoint B = srcTriangle.B; TPoint C = srcTriangle.C; px = MIN2(A.x, MIN2(B.x, C.x)); py = MIN2(A.y, MIN2(B.y, C.y)); mw = (int)(MAX2(A.x,MAX2(B.x, C.x)) - px); mh = (int)(MAX2(A.y,MAX2(B.y, C.y)) - py); k = ratio * 255 / 100; nk = 255 - k; for(j = 0; j < mh; j++) { for(i = 0; i < mw; i++) { curPoint.x = CLIP3(i + px, 0, width - 1); curPoint.y = CLIP3(j + py, 0, height - 1); if (WARP_JudgePointInTriangleOrNot(curPoint,srcTriangle)) { if(pFaceLabel[(int)curPoint.x + (int)curPoint.y * width] == 255) continue; TriangleTransform(curPoint, srcTriangle,dstTriangle, &cx, &cy); x0 = (int)CLIP3(floor(cx), 0, dWidth - 2); y0 = (int)CLIP3(floor(cy), 0, dHeight - 2); index_x0y0 = (x0 << 2) + y0 * dStride; index_x1y0 = index_x0y0 + 4; index_x0y1 = index_x0y0 + dStride; index_x1y1 = index_x0y1 + 4; disX = cx - x0; disY = cy - y0; tmp1 = dstData[index_x0y0] + disX *(dstData[index_x1y0] - dstData[index_x0y0]); tmp2 = dstData[index_x0y1] + disX *(dstData[index_x1y1] - dstData[index_x0y1]); AB = tmp1 + disY * (tmp2 - tmp1); tmp1 = dstData[index_x0y0 + 1] + disX *(dstData[index_x1y0 + 1] - dstData[index_x0y0 + 1]); tmp2 = dstData[index_x0y1 + 1] + disX *(dstData[index_x1y1 + 1] - dstData[index_x0y1 + 1]); AG = tmp1 + disY * (tmp2 - tmp1); tmp1 = dstData[index_x0y0 + 2] + disX *(dstData[index_x1y0 + 2] - dstData[index_x0y0 + 2]); tmp2 = dstData[index_x0y1 + 2] + disX *(dstData[index_x1y1 + 2] - dstData[index_x0y1 + 2]); AR = tmp1 + disY * (tmp2 - tmp1); tmp1 = dstData[index_x0y0 + 3] + disX *(dstData[index_x1y0 + 3] - dstData[index_x0y0 + 3]); tmp2 = dstData[index_x0y1 + 3] + disX *(dstData[index_x1y1 + 3] - dstData[index_x0y1 + 3]); AA = tmp1 + disY * (tmp2 - tmp1); nx = (int)CLIP3(i + px, 0, width - 1); ny = (int)CLIP3(j + py, 0, height - 1); pos = (nx << 2) + ny * stride; r = (int)AR; g = (int)AG; b = (int)AB; a = (int)AA; temp = 255 - a; if(b > 220 && g > 220 && r > 220 && a != 0) { srcData[pos + 3] = 0; } b = CLIP3((b * a + temp * srcData[pos + 0]) / 255, 0, 255); g = CLIP3((g * a + temp * srcData[pos + 1]) / 255, 0, 255); r = CLIP3((r * a + temp * srcData[pos + 2]) / 255, 0, 255); srcData[pos + 0] = CLIP3((b * k + nk * srcData[pos + 0]) >> 8, 0, 255); srcData[pos + 1] = CLIP3((g * k + nk * srcData[pos + 1]) >> 8, 0, 255); srcData[pos + 2] = CLIP3((r * k + nk * srcData[pos + 2]) >> 8, 0, 255); //srcData[pos + 3] = 255;///////////////////////////////////////////////////////////// pFaceLabel[(int)curPoint.x + (int)curPoint.y * width] = 255; } } } free(tempData); return 0; } int f_FaceMesh(unsigned char* srcData, int width ,int height, int stride, unsigned char* maskData, int mWidth, int mHeight, int mStride, int srcFacePointsAll[101 * 2], int mskFacePointsAll[101 * 2 + 8 * 2], int ratio, int method) { int ret = 0; Triangle mskTriangle[191], srcTriangle[191]; GetTriangle(mskFacePointsAll,mskTriangle); int maxx = 0, maxy = 0, minx = 10000, miny = 10000; for(int i = 0; i < 101; i++) { maxx = maxx > srcFacePointsAll[(i << 1)] ? maxx : srcFacePointsAll[(i << 1)]; maxy = maxy > srcFacePointsAll[(i << 1) + 1] ? maxy : srcFacePointsAll[(i << 1) + 1]; minx = minx < srcFacePointsAll[(i << 1)] ? minx : srcFacePointsAll[(i << 1)]; miny = miny < srcFacePointsAll[(i << 1) + 1] ? miny : srcFacePointsAll[(i << 1) + 1]; } int tx, ty, roiWidth; tx = minx; ty = miny; roiWidth = MAX2(abs(maxx - minx),abs(maxy - miny)); int roiHeight = roiWidth; int k = 2; int px = MAX2(tx - roiWidth / k, 0); int py = MAX2(ty - roiWidth / k, 0); roiWidth = CLIP3(tx - px + roiWidth + roiWidth / k, 0, width - 1); roiHeight = CLIP3(ty - py + roiHeight + roiWidth / k, 0, height - 1); int rw = MIN2(roiWidth, roiHeight); roiWidth = roiHeight = rw; tx = px; ty = py; int facePointsAll[202 + 16]; for(int i = 0; i < 202; i++) { facePointsAll[i] = srcFacePointsAll[i]; } facePointsAll[202] = tx; facePointsAll[203] = ty; facePointsAll[204] = tx; facePointsAll[205] = ty + (roiHeight >> 1); facePointsAll[206] = tx; facePointsAll[207] = ty + roiHeight; facePointsAll[208] = tx + (roiWidth >> 1); facePointsAll[209] = ty + roiHeight; facePointsAll[210] = tx + roiWidth; facePointsAll[211] = ty + roiHeight; facePointsAll[212] = tx + roiWidth; facePointsAll[213] = ty + (roiHeight >> 1); facePointsAll[214] = tx + roiWidth; facePointsAll[215] = ty; facePointsAll[216] = tx + (roiWidth >> 1); facePointsAll[217] = ty; GetTriangle(facePointsAll, srcTriangle); unsigned char* pFaceLabel = (unsigned char*)malloc(sizeof(unsigned char) * width * height); memset(pFaceLabel, 0, sizeof(unsigned char) * width * height); for(int i = 0; i < 191-15; i++) { TriangleProcess(srcTriangle[i], srcData, width ,height, stride, mskTriangle[i], maskData, mWidth, mHeight, mStride, pFaceLabel, ratio); } free(pFaceLabel); return ret; }; //SpecialFace filter api int f_Test(unsigned char* srcData, int width ,int height, int stride, unsigned char* maskData, int mWidth, int mHeight, int mStride, int srcFacePointsAll[101 * 2], int mskFacePointsAll[101 * 2 + 8 * 2], int ratio) { int ret = 0; unsigned char* pMask = maskData; for(int j = 0; j < mHeight; j++) { for(int i = 0; i < mWidth; i++) { if(pMask[3] == 0) pMask[0] = pMask[1] = pMask[2] = 0; pMask += 4; } } //Process unsigned char* tempData = (unsigned char*)malloc(sizeof(unsigned char) * height * stride); memcpy(tempData, srcData, sizeof(unsigned char) * height * stride); ret = f_FaceMesh(srcData, width, height, stride, maskData, mWidth, mHeight, mStride, srcFacePointsAll, mskFacePointsAll, ratio, 0); int nx = 0, ny = 0, pos = 0, dis; unsigned char* pSrc = srcData; dis = srcFacePointsAll[2 * 51] - srcFacePointsAll[2 * 45]; for(int j = 0; j < height; j++) { for(int i = 0; i < width; i++) { if(pSrc[3] == 0) { nx = (int)CLIP3(i - dis, 0, width - 1); ny = (int)CLIP3(j, 0, height - 1); pos = (nx << 2) + ny * stride; pSrc[0] = (int)tempData[pos + 0]; pSrc[1] = (int)tempData[pos + 1]; pSrc[2] = (int)tempData[pos + 2]; pSrc[3] = 255; } pSrc += 4; } } free(tempData); return ret; };
最後,本人QQ1358009172,微信公眾號:SF影象演算法