摘要
本文介紹模糊臉部的功能邏輯和實現方式,實現方式會盡可能的使用蘋果提供的 API,保證功能高效率和簡潔。
邏輯
模糊臉部的邏輯主要有兩個流程,就是先找到臉部,然后模糊臉部,那么就引申出這兩個實現問題:
- 如何正確找到臉部區域?
- 如何只模糊臉部區域?
依次解決這兩個問題,那么這個功能就已經輕松實現了。
實現
實現功能方式有很多,這里只是分享一下自己的實現方式。主要借鑒 Core Image 中的方法。
找臉部區域
使用 CIDetector 類來查找圖片中的臉部,雖然文檔中說明可以找到比如鼻子更具體的部位,但是一直沒有找到實現方式,它的識別成功率相對比較高,不是百分之百。
代碼邏輯歸納為:
- 通過
CIDetector類獲取圖片中的所有臉部區域 - 通過
CIFilter.sourceOverCompositing函數繪制出存在所有臉部區域的 mask 圖
// MARK: - 獲取圖像中面部區域數據
func getFaceData(from image: UIImage?) -> CIImage? {
guard image != nil, let image = CIImage(image: image!) else { return nil }
// CIDetectorTypeFace
let detector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: nil)
guard let faceArray = detector?.features(in: image, options: nil) else { return nil}
var maskImage: CIImage? = nil
for face in faceArray {
let bounds = face.bounds
let centerX = bounds.origin.x + bounds.size.width * 0.4
let centerY = bounds.origin.y + bounds.size.height * 0.5
let radius = min(bounds.size.width, bounds.size.height) * 0.5
let gaussion = CIFilter.radialGradient(inputCenter: CIVector(x: centerX, y: centerY),
inputRadius0: NSNumber(value: Int(radius)),
inputRadius1: NSNumber(value: Int(radius+1)),
inputColor0: CIColor(red: 0, green: 1, blue: 0, alpha: 1),
inputColor1: CIColor(red: 0, green: 0, blue: 0, alpha: 0))
guard let gaussianImage = gaussion?.outputImage else { continue }
if maskImage == nil {
maskImage = gaussianImage
} else {
maskImage = CIFilter.sourceOverCompositing(inputImage: gaussianImage, inputBackgroundImage: maskImage!)?.outputImage
}
}
return maskImage
}
模糊臉部區域
上面步驟獲取到有臉部區域的 mask 圖,下面就對臉部進行模糊。這里使用 使用 CISourceOverCompositing 處理臉部模糊。
使用 blendWithMask 函數時,會發現要傳入 3 張 image 對象,但是到目前只有一張原圖和一張臉部的 mask 圖,那么第三張圖是什么呢?
這里使用的第三張圖是一張將原圖通過 gaussianBlur 之后的圖片。然后在使用 blendWithMask 合成后獲得,那么這三張圖放置有什么講究呢?下面簡單總結一下:
inputImage: 放置整體被高斯模糊后的圖inputBackgroundImage: 放置原圖inputMaskImage: 放置獲取到臉部的 mask 圖
通過效果看這三張圖是這樣處理,inputBackgroundImage 和 inputMaskImage 組合獲得到臉部區域被扣去的圖片,然后在這張圖下面放置 inputImage 圖,就能得到臉部被高斯模糊的圖片了。
// MARK: - 模糊人臉
func blurVariable(inputImage: UIImage?, maskInputImage: CIImage) -> UIImage? {
guard let image = inputImage, let ciImg = CIImage(image: image) else { return nil }
let blur = CIFilter.gaussianBlur(inputImage: ciImg, inputRadius: 8)
guard let blurImage = blur?.outputImage else { return nil}
let maskedVariableFilter = CIFilter.blendWithMask(inputImage: blurImage, inputBackgroundImage: ciImg, inputMaskImage: maskInputImage)
if let outputImg = maskedVariableFilter?.outputImage {
return UIImage(ciImage: outputImg.oriented(image.imageOrientation))
}
return nil
}
題外話
時間倉促,說的東西可能不全面,在你實現過程中遇到什么問題,評論區給我留言,我會盡快回復
