Swift-技巧(二)模糊臉部功能
摘要
本文介紹模糊臉部的功能邏輯和實現方式,實現方式會儘可能的使用蘋果提供的 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
}
題外話
時間倉促,說的東西可能不全面,在你實現過程中遇到什麼問題,評論區給我留言,我會儘快回復