iOS AI相機App開發(fā)教程巨缘,02 實現瘦臉功能

瘦臉功能實現原理

瘦臉

人臉檢測

首先需要對照片進行人臉檢測,檢測出人臉輪廓位置察皇。人臉檢測的方法非常多。在“魔法相機”中使用了 iOS Vision 庫中的 VNDetectFaceLandmarksRequest 方法來進行人臉檢測泽台。使用這個方法可以充分利用iPhone的AI能力什荣,并且檢測效果也非常好。

MLS圖像變形算法

MLS變形算法又叫做移動最小二乘變形算法怀酷,該算法引用量較高稻爬,效果相對較好,在變形過程中幾乎毫無違和感蜕依,這也是MLS的優(yōu)勢所在桅锄。

MLS變形算法在美顏中的人臉變形特效或者圖像變形特效中有著較多的應用,比如“天天P圖”中的瘋狂換臉特效样眠,就是以MLS變形為基礎開發(fā)設計的友瘤。”魔法相機“的瘦臉功能和變老功能也使用了MLS算法檐束。

實現原理

使用人臉檢測獲得人臉眼睛到下巴的輪廓點辫秧,并在圖片邊緣進行錨點,然后通過MLS變形被丧,將人像下半部分的臉和下巴的錨點做適當的收縮盟戏。

代碼講解

// 瘦臉特效方法
func DoFaceThin(uiImage: UIImage, completionHandler: @escaping CompletionHandle) {
        let image = CIImage(image: uiImage)
        let weakSelf = self
        let width = uiImage.size.width
        let height = uiImage.size.height
        
        // 人臉檢測完成回調方法
        let faceRequest = VNDetectFaceLandmarksRequest() { request, error in
            guard let results = request.results as? [VNFaceObservation] else {
             ...
            }
            let face = results.first
             ...
            guard let boundingRect = face?.boundingBox else {
               ...
            }

           // 獲得人臉位置盒子
            var boundingBox = CGRect(x:CGFloat(boundingRect.origin.x * width),
                                     y:CGFloat(height - ((boundingRect.origin.y + boundingRect.size.height) * height)),
                                     width: CGFloat(boundingRect.size.width * width),
                                     height: CGFloat(boundingRect.size.height * height))
            // 錨點坐標
            var points: [CGPoint] = []
            // 變換后坐標
            var toPoints: [CGPoint] = []

            // 將人臉輪廓點添加到錨點數組中
            for point in face!.landmarks!.faceContour!.normalizedPoints {
               let p = CGPoint(x: CGFloat(point.x * boundingBox.width + boundingBox.origin.x),
                               y: CGFloat((1.0 - point.y) * boundingBox.height + boundingBox.origin.y))
                points.append(p)
                toPoints.append(p)
            }
            
            // 將圖片邊緣添加到錨點數組里
            for i in stride(from: 0, to: Int(height) ,by: 80) {
                let p1 = CGPoint(x: 0, y:CGFloat(i))
                let p2 = CGPoint(x: width, y:CGFloat(i))
                points.append(p1)
                toPoints.append(p1)
                points.append(p2)
                toPoints.append(p2)
            }
            // 將圖片四角添加到錨點列表中
            let bottomLeft = CGPoint(x: 0, y: height)
            let bottomRight = CGPoint(x: width, y: height)
            points.append(bottomLeft)
            toPoints.append(bottomLeft)
            points.append(bottomRight)
            toPoints.append(bottomRight)
            

            // 對人臉輪廓進行收縮,實現瘦臉效果
            for index in 1...6 {
                let idx1 = 8 - index
                let idx2 = 8 + index
                
                let w = points[idx1].x - points[idx2].x
                
                let padding = w/CGFloat(8+index*2)/2
                
                toPoints[idx1].x -= padding
                toPoints[idx2].x += padding
            }
            
            // MLS 變換實例晚碾,傳入錨點和目標點坐標抓半,對圖片進行變換
            let thinFaceOperation = ThinFaceOperation()
            thinFaceOperation.setupData(image: uiImage, landmarks: points, toPoints: toPoints)
            
            let imageInput = PictureInput(image: uiImage)
            
            let imageOutput = PictureOutput()
            imageOutput.imageAvailableCallback = { image in
                completionHandler(image)
                weakSelf.lockFx.unlock()
            }
            
            imageInput --> thinFaceOperation --> imageOutput
            imageInput.processImage(synchronously:true)
        }
        
        // 執(zhí)行人臉檢測
        let handler = VNImageRequestHandler(ciImage: image!,options: [:])
        DispatchQueue.global(qos: .userInitiated).async {
            try? handler.perform([faceRequest])
        }
    }

MSL變換需要兩組數據喂急,分別是錨點(原始點)的坐標位置數組和目標點(變換后的點)的坐標位置數組格嘁。
實現 VNDetectFaceLandmarksRequest 回調方法,在回調中處理人臉數據廊移。
將圖片的邊緣和4角坐標也都傳入錨點和目標點數組糕簿,這樣可以防止圖片邊緣因為MSL變換產生畸變探入。
將人類數據傳入錨點和目標點數組,并處理目標點坐標懂诗,使臉部收縮變瘦蜂嗽。
最后調用MSL方法實例,完成變換操作殃恒。

瘦臉特效完整代碼 MagicCamera/Vision/ThinFace.swift

MSL算法的具體實現可以看源碼文件: MagicCamera/Vision/MlsOperation/MovingLeastSquareHelper.mm

魔法相機項目

項目地址: william0wang/MagicCamera (github.com)
系列教程: 魔法相機 - 開發(fā)教程

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末植旧,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子离唐,更是在濱河造成了極大的恐慌病附,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件亥鬓,死亡現場離奇詭異完沪,居然都是意外死亡,警方通過查閱死者的電腦和手機嵌戈,發(fā)現死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門覆积,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人熟呛,你說我怎么就攤上這事宽档。” “怎么了庵朝?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵雌贱,是天一觀的道長。 經常有香客問我偿短,道長欣孤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任昔逗,我火速辦了婚禮降传,結果婚禮上,老公的妹妹穿的比我還像新娘勾怒。我一直安慰自己婆排,他們只是感情好,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布笔链。 她就那樣靜靜地躺著段只,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鉴扫。 梳的紋絲不亂的頭發(fā)上赞枕,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音,去河邊找鬼炕婶。 笑死姐赡,一個胖子當著我的面吹牛,可吹牛的內容都是我干的柠掂。 我是一名探鬼主播项滑,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼涯贞!你這毒婦竟也來了枪狂?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤宋渔,失蹤者是張志新(化名)和其女友劉穎摘完,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體傻谁,經...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡孝治,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了审磁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谈飒。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖态蒂,靈堂內的尸體忽然破棺而出杭措,到底是詐尸還是另有隱情,我是刑警寧澤钾恢,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布手素,位于F島的核電站,受9級特大地震影響瘩蚪,放射性物質發(fā)生泄漏泉懦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一疹瘦、第九天 我趴在偏房一處隱蔽的房頂上張望崩哩。 院中可真熱鬧,春花似錦言沐、人聲如沸邓嘹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汹押。三九已至,卻和暖如春起便,著一層夾襖步出監(jiān)牢的瞬間棚贾,已是汗流浹背窖维。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鸟悴,地道東北人陈辱。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓奖年,卻偏偏與公主長得像细诸,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子陋守,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內容