处理后 AVFoundation 相机图像质量下降

AVFoundation camera image quality degraded upon processing

我制作了一个 AVFoundation 相机来根据@fsaint 的回答裁剪方形图像:。照片的尺寸很好,效果很好。然而,图像质量明显下降(见下图:第一张图像是显示良好分辨率的预览层,第二张是捕获的质量下降的图像)。它肯定与 processImage: 中发生的事情有关,因为没有它图像分辨率很好,只是宽高比不正确。关于图像处理的文档非常简单,非常感谢任何见解!!

正在设置相机:

func setUpCamera() {

      captureSession = AVCaptureSession()
      captureSession!.sessionPreset = AVCaptureSessionPresetPhoto
      let backCamera = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
      if ((backCamera?.hasFlash) != nil) {
         do { 
            try backCamera.lockForConfiguration()
            backCamera.flashMode = AVCaptureFlashMode.Auto
            backCamera.unlockForConfiguration()
         } catch {
            // error handling
         }
      }
      var error: NSError?
      var input: AVCaptureDeviceInput!
      do {
         input = try AVCaptureDeviceInput(device: backCamera)
      } catch let error1 as NSError {
         error = error1
         input = nil
      }
      if error == nil && captureSession!.canAddInput(input) {
         captureSession!.addInput(input)
         stillImageOutput = AVCaptureStillImageOutput()
         stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
         if captureSession!.canAddOutput(stillImageOutput) {
            captureSession!.sessionPreset = AVCaptureSessionPresetHigh;
            captureSession!.addOutput(stillImageOutput)
            previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
            previewLayer!.videoGravity = AVLayerVideoGravityResizeAspectFill
            previewLayer!.connection?.videoOrientation = AVCaptureVideoOrientation.Portrait
            previewVideoView.layer.addSublayer(previewLayer!)
            captureSession!.startRunning()
         }
      }
   }

抓拍照片:

@IBAction func onSnapPhotoButtonPressed(sender: UIButton) {

      if let videoConnection = self.stillImageOutput!.connectionWithMediaType(AVMediaTypeVideo) {
         videoConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
self.stillImageOutput?.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: {(sampleBuffer, error) in

            if (sampleBuffer != nil) {

               let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
               let dataProvider = CGDataProviderCreateWithCFData(imageData)
               let cgImageRef = CGImageCreateWithJPEGDataProvider(dataProvider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)

               let image = UIImage(CGImage: cgImageRef!, scale: 1.0, orientation: UIImageOrientation.Right)   
               self.processImage(image)
               self.clearPhotoButton.hidden = false
               self.nextButton.hidden = false
               self.view.bringSubviewToFront(self.imageView)
            }
         })
      }
   }

将图像处理成正方形:

  func processImage(image:UIImage) {

      let deviceScreen = previewLayer?.bounds
      let width:CGFloat = (deviceScreen?.size.width)!
      UIGraphicsBeginImageContext(CGSizeMake(width, width))
      let aspectRatio:CGFloat = image.size.height * width / image.size.width
      image.drawInRect(CGRectMake(0, -(aspectRatio - width) / 2.0, width, aspectRatio))
      let smallImage = UIGraphicsGetImageFromCurrentImageContext()
      UIGraphicsEndImageContext()
      let cropRect = CGRectMake(0, 0, width, width)
      let imageRef:CGImageRef = CGImageCreateWithImageInRect(smallImage.CGImage, cropRect)!
      imageView.image = UIImage(CGImage: imageRef)
   }

您的 processImage() 功能有一些问题。

首先,您要使用 UIGraphicsBeginImageContext() 创建一个新的图形上下文。

According to the Apple docs on this function:

This function is equivalent to calling the UIGraphicsBeginImageContextWithOptions function with the opaque parameter set to NO and a scale factor of 1.0.

因为比例因子是 1.0,它在屏幕上显示时看起来会像素化,因为屏幕的分辨率(很可能)更高。

您想使用 UIGraphicsBeginImageContextWithOptions() 函数,并为比例因子传递 0.0 根据此函数的文档,对于scale 参数:

If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.

例如:

UIGraphicsBeginImageContextWithOptions(CGSizeMake(width, width), NO, 0.0);

您的输出现在应该看起来漂亮而清晰,因为它是使用与屏幕相同的比例呈现的。


其次,你传入的宽度有问题

let width:CGFloat = (deviceScreen?.size.width)!
UIGraphicsBeginImageContext(CGSizeMake(width, width))

这里不应该传入屏幕的宽度,应该是图片的宽度。例如:

let width:CGFloat = image.size.width

然后您必须更改 aspectRatio 变量以获取屏幕宽度,例如:

let aspectRatio:CGFloat = image.size.height * (deviceScreen?.size.width)! / image.size.width

第三,您可以显着简化裁剪功能。

func processImage(image:UIImage) {

    let screenWidth = UIScreen.mainScreen().bounds.size.width

    let width:CGFloat = image.size.width
    let height:CGFloat = image.size.height

    let aspectRatio = screenWidth/width;

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(screenWidth, screenWidth), false, 0.0) // create context
    let ctx = UIGraphicsGetCurrentContext()

    CGContextTranslateCTM(ctx, 0, (screenWidth-(aspectRatio*height))*0.5) // shift context up, to create a sqaured 'frame' for your image to be drawn in

    image.drawInRect(CGRect(origin:CGPointZero, size: CGSize(width:screenWidth, height:height*aspectRatio))) // draw image

    let img = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    imageView.image = img
}

无需二次绘制图像,只需将上下文平移,再绘制图像即可。