瘦臉功能實現原理
人臉檢測
首先需要對照片進行人臉檢測,檢測出人臉輪廓位置察皇。人臉檢測的方法非常多。在“魔法相機”中使用了 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ā)教程