对 AVCaptureStillImageOutput 应用遮罩

Apply a mask to AVCaptureStillImageOutput

我正在做一个项目,我想遮盖用户刚刚用他们的相机拍摄的照片。遮罩以特定的纵横比创建,以将信箱添加到照片。

我可以成功创建图像、创建蒙版并将两者都保存到相机胶卷,但我无法将蒙版应用于图像。这是我现在的代码

func takePhoto () {
  dispatch_async(self.sessionQueue) { () -> Void in
    if let photoOutput = self.output as? AVCaptureStillImageOutput {
      photoOutput.captureStillImageAsynchronouslyFromConnection(self.outputConnection) { (imageDataSampleBuffer, err) -> Void in
        if err == nil {
          let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer)
          let image = UIImage(data: imageData)

          if let _ = image {
            let maskedImage = self.maskImage(image!)

            print("masked image: \(maskedImage)")

            self.savePhotoToLibrary(maskedImage)
          }
        } else {
          print("Error while capturing the image: \(err)")
        }
      }
    }
  }
}

func maskImage (image: UIImage) -> UIImage {
  let mask = createImageMask(image)

  let maskedImage = CGImageCreateWithMask(image.CGImage, mask!)

  return UIImage(CGImage: maskedImage!)
}

func createImageMask (image: UIImage) -> CGImage? {
  let width = image.size.width
  let height = width / CGFloat(store.state.aspect.rawValue)
  let x = CGFloat(0.0)
  let y = (image.size.height - height) / 2
  let maskRect = CGRectMake(0.0, 0.0, image.size.width, image.size.height)
  let maskContents = CGRectMake(x, y, width, height)

  var color = UIColor(white: 1.0, alpha: 0.0)

  UIGraphicsBeginImageContextWithOptions(CGSizeMake(maskRect.size.width, maskRect.size.height), false, 0.0)

  color.setFill()
  UIRectFill(maskRect)

  color = UIColor(white: 0.0, alpha: 1.0)
  color.setFill()
  UIRectFill(maskContents)

  let maskImage = UIGraphicsGetImageFromCurrentImageContext()

  UIGraphicsEndImageContext()

  print("mask: \(maskImage)")
  savePhotoToLibrary(image)
  savePhotoToLibrary(maskImage)

  let mask = CGImageMaskCreate(
    CGImageGetWidth(maskImage.CGImage),
    CGImageGetHeight(maskImage.CGImage),
    CGImageGetBitsPerComponent(maskImage.CGImage),
    CGImageGetBitsPerPixel(maskImage.CGImage),
    CGImageGetBytesPerRow(maskImage.CGImage),
    CGImageGetDataProvider(maskImage.CGImage),
    nil,
    false)

  return mask
}

据我了解,CGImageCreateWithMask 要求要屏蔽的图像具有 Alpha 通道。我已经尝试了我在这里看到的所有方法来为 jpeg 表示添加 alpha 通道,但我没有任何运气。任何帮助都是超级的。

这可能是一个错误,也可能只是有点误导。 CGImageCreateWithMask() 实际上并没有修改图像 - 它只是将遮罩数据与图像数据相关联,并在将图像绘制到上下文(例如在 UIImageView 中)时使用遮罩,但是 不是 当你将图像保存到磁盘时。

有几种方法可以生成蒙版图像的 "rendered" 版本,但如果我理解你的意图,你并不是真的想要 "mask" ...你想要一封信-图像的盒装版本。

这是一个选项,可以有效地在图像的顶部和底部绘制黑条(如果您不想要黑色,条/框颜色是可选参数)。然后您可以保存修改后的图像。

在上面的代码中,替换

  let maskedImage = self.maskImage(image!)

  let height = image.size.width / CGFloat(store.state.aspect.rawValue)
  let maskedImage = self.doLetterBox(image!, visibleHeight: height)

并添加此功能:

func doLetterBox(sourceImage: UIImage, visibleHeight: CGFloat, frameColor: UIColor?=UIColor.blackColor()) -> UIImage! {

    // local rect based on sourceImage size
    let imageRect: CGRect = CGRectMake(0.0, 0.0, sourceImage.size.width, sourceImage.size.height)

    // rect for "visible" part of letter-boxed image
    let clipRect: CGRect = CGRectMake(0.0, (imageRect.size.height - visibleHeight) / 2.0, imageRect.size.width, visibleHeight)

    // setup the image context, using sourceImage size
    UIGraphicsBeginImageContextWithOptions(imageRect.size, true, UIScreen.mainScreen().scale)

    let ctx: CGContextRef = UIGraphicsGetCurrentContext()!
    CGContextSaveGState(ctx)

    // fill new empty image with frameColor (defaults to black)
    CGContextSetFillColorWithColor(ctx, frameColor?.CGColor)
    CGContextFillRect(ctx, imageRect)

    // set Clipping rectangle to allow drawing only in desired area
    UIRectClip(clipRect)

    // draw the sourceImage to full-image-size (the letter-boxed portion will be clipped)
    sourceImage.drawInRect(imageRect)

    // get new letter-boxed image
    let resultImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()

    // clean up
    CGContextRestoreGState(ctx)
    UIGraphicsEndImageContext()

    return resultImage

}