1. 程式人生 > >影象處理演算法之瘦臉及放大眼睛

影象處理演算法之瘦臉及放大眼睛

       現在很多影象美顏app,處理後不但使人物面板變得平滑、白皙,還會稍微瘦下臉、放大眼睛,給人眼前一亮的感覺。這其中涉及人臉檢測及特徵點提取演算法,一般提取68個特徵點就足夠了,同時也涉及影象區域性變形演算法。這也是兩個研究方向,前者主要是計算機視覺,後者是影象處理。但隨著深度學習的大熱及在多個領域的成功應用,很多影象處理技術也開始採用深度學習演算法實現突破,比如基於深度學習的降噪、超解析度、非真實感繪製等等技術。深度學習大有在計算機視覺、影象處理一統之態勢。言歸正傳,本文主要還是介紹瘦臉及放大眼睛所用到的影象區域性變形演算法。主要參考:互動式影象變形演算法一文 。看過該文章後,自然瞭解瘦臉其實用的是影象區域性平移變形,放大眼睛,用的是影象區域性縮放變形。同時,文章還闡述了影象區域性旋轉變形的演算法原理及思路。
       瘦臉及放大眼睛的前提是需要檢測到人臉,並提取特徵點。談到影象變形,最基礎的思路是:由變形前座標,根據變形對映關係,得到變形後坐標。這其中變形對映關係是最關鍵的,不同的對映關係,將得到不同的變形效果。平移、縮放、旋轉,對應的是不同的對映關係,即不同的變換公式。當然實際在計算過程中,用的是逆變換,即由變形後坐標,根據逆變換公式反算變形前座標,然後插值得到該座標rgb畫素值,將該rgb值作為變形後坐標對應的畫素值。這樣才能保證變形後的影象是連續、完整的。

下面簡單講一下影象區域性平移變形,該變形稍微複雜一些,公式如下:

                                       


公式中,由於主要是畫素點位置計算,因此涉及一些向量運算,不過比較簡單。其實上面公式就是逆變換公式了,x是變換後的位置,u是原座標位置。整個計算在以c為圓心,r為半徑的圓內進行。因為是互動式影象區域性變形,所以c也可以看做滑鼠點下時的座標,而m為滑鼠移動一段距離後擡起時的座標,這樣c和m就決定了變形方向。下面是示例程式碼,公式結合程式碼一起看,應該很快能弄明白。
void LocalTranslationWarp(Mat &img, int warpX, int warpY, int warpW, int warpH, int directionX, int directionY, double warpCoef)
{
	RestrictBounds(warpX, warpY, warpW, warpH);

	Mat imgCopy;
	copyMakeBorder(img, imgCopy, 0, 1, 0, 1, BORDER_REPLICATE);

	Point center(warpX + (warpW>>1), warpY + (warpH>>1));
	double radius = (warpW < warpH) ? (warpW >> 1) : (warpH >> 1);
	radius = radius * radius;

	// 平移方向向量/模
	double transVecX = directionX - center.x;
	double transVecY = directionY - center.y;
	double transVecModel = transVecX*transVecX + transVecY*transVecY;

	// 水平/垂直增量//對映後位置與原位置
	double dx = 0, dy = 0, posX = 0.0, posY = 0.0, posU = 0.0, posV = 0.0;
	// 點到圓心距離/平移比例
	double distance = 0.0, ratio = 0.0;
	// 插值位置
	int startU = 0, startV = 0;
	double alpha = 0.0, beta = 0.0;

	int maxRow = warpY + warpH;
	int maxCol = warpX + warpW;
	uchar* pImg = NULL;
	for (int i = warpY; i < maxRow; i++)
	{
		pImg = img.data + img.step * i;
		for (int j = warpX; j < maxCol; j++)
		{
			posX = j;
			posY = i;
			dx = posX - center.x;
			dy = posY - center.y;
			distance = dx*dx + dy*dy;
			if (distance < radius)
			{
				ratio = (radius - distance) / (radius - distance + transVecModel * warpCoef);
				posU = posX - ratio * ratio * transVecX;
				posV = posY - ratio * ratio * transVecY;

				startU = (int)posU;
				startV = (int)posV;
				alpha = posU - startU;
				beta  = posV - startV;
				BilinearInter(imgCopy, startU, startV, alpha, beta, pImg[3*j], pImg[3*j + 1], pImg[3*j + 2]);
			}
		}
	}
}
可以看到,只有圓形選區內的影象才進行變形。越靠近圓心,變形越大,反之變形越小。對於影象縮放變形及旋轉變形,公式比較簡單,實現起來也容易一些,本文就不在詳細講解了。應用上面演算法,簡單的瘦臉效果如下:     
       參考文獻: