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