1. 程式人生 > >深度學習AI美顏系列---SpecialFace特效濾鏡

深度學習AI美顏系列---SpecialFace特效濾鏡

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影象演算法