【问题标题】:AVFoundation camera image quality degraded upon processing处理后 AVFoundation 相机图像质量下降
【发布时间】:2016-02-12 19:55:00
【问题描述】:

我制作了一个 AVFoundation 相机来根据@fsaint 的答案裁剪方形图像:Cropping AVCaptureVideoPreviewLayer output to a square。照片的尺寸很棒,效果很好。但是图像质量明显下降(见下图:第一张图像是显示良好分辨率的预览层,第二张是捕获的退化图像)。这肯定与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)
   }

【问题讨论】:

    标签: ios swift camera avfoundation


    【解决方案1】:

    您的 processImage() 函数有一些问题。

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

    According to the Apple docs on this function:

    此函数相当于调用 UIGraphicsBeginImageContextWithOptions 函数,其中 opaque 参数设置为 NO,比例因子为 1.0。

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

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

    如果您指定值 0.0,则比例因子设置为设备主屏幕的比例因子。

    例如:

    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
    }
    

    不需要绘制两次图像,只需将上下文向上翻译,然后绘制图像。

    【讨论】:

    • 感谢@originaluser2 的回复!我尝试了您的解决方案,图像分辨率很清晰,但是图像现在非常放大。关于为什么会发生这种情况的任何想法?再次感谢!
    • @tahoecoop 是的,我刚刚注意到您正在使用屏幕大小而不是图像大小创建上下文。我已经编辑了我的答案。
    • 我明白你在说什么,我尝试了你的编辑。不幸的是,它仍然像以前一样放大。当我使用 `letcropRect = CGRectMake(0, 0, width, width)` 时,我是否弄乱了尺寸?再次感谢!!
    • @tahoecoop 啊是的,其实你应该简单地将width变量更改为图像宽度,然后将aspectRatio变量调整为屏幕宽度。
    • @tahoecoop 实际上,我刚刚重新设计了您的功能,它现在应该可以按照您的意愿进行操作了。
    猜你喜欢
    • 1970-01-01
    • 2017-05-06
    • 1970-01-01
    • 2019-08-04
    • 1970-01-01
    • 2010-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多