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
 }

題外話

時間倉促,說的東西可能不全面,在你實現過程中遇到什麼問題,評論區給我留言,我會盡快回復

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章