AVCAPTURE 图像方向

AVCAPTURE image orientation

我有一个允许用户拍照的视图控制器。我将 avcapture 边界设置为屏幕上视图的边界。

在这个视图之上我有一个集合视图。所以用户可以拍摄多张照片,然后将它们添加到上面的集合视图中。

我无法在上面的预览中显示正确的方向。

代码如下:

@IBOutlet weak var imagePreviews: UICollectionView!
@IBOutlet weak var imgPreview: UIView!

var session: AVCaptureSession?
var stillImageOutput: AVCaptureStillImageOutput?
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
var images: [UIImage] = [UIImage]()

var isLandscapeLeft     : Bool = false
var isLandscapeRight    : Bool = false
var isPortrait          : Bool = false
var isPortraitUpsideDown: Bool = false

@IBAction func capture(_ sender: UIButton)
    {
    if let videoConnection = stillImageOutput?.connection(withMediaType: AVMediaTypeVideo)
            {
                stillImageOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (sampleBuffer, error) in
                    if sampleBuffer != nil {
                        if let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer), let dataProvider = CGDataProvider(data: imageData as CFData), let cgImageRef = CGImage(jpegDataProviderSource: dataProvider, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)
                        {
                            var image: UIImage

                            if (self.isLandscapeLeft || self.isLandscapeRight)
                            {
                                image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: self.isLandscapeLeft ? UIImageOrientation.left : UIImageOrientation.right)
                            } else {
                                image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: self.isPortrait ? UIImageOrientation.up : UIImageOrientation.down)
                            }

                            image = self.fixOrientation(img: image)
                            self.images.append(image)


                            DispatchQueue.main.async {
                                self.imagePreviews.reloadData()
                            }
                        }
                    }
                })
            }
        }
   } 

override func viewDidLayoutSubviews() {

    super.viewDidLayoutSubviews()

    if let connection =  self.videoPreviewLayer?.connection  {

        let currentDevice: UIDevice = UIDevice.current

        let orientation: UIDeviceOrientation = currentDevice.orientation

        let previewLayerConnection : AVCaptureConnection = connection

        if (previewLayerConnection.isVideoOrientationSupported) {

            switch (orientation) {
            case .portrait:

                isLandscapeLeft      = false
                isLandscapeRight     = false
                isPortrait           = true
                isPortraitUpsideDown = false
                updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait)
                break

            case .landscapeRight:

                isLandscapeLeft      = false
                isLandscapeRight     = true
                isPortrait           = false
                isPortraitUpsideDown = false
                updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeRight)
                break

            case .landscapeLeft:

                isLandscapeLeft      = true
                isLandscapeRight     = false
                isPortrait           = false
                isPortraitUpsideDown = false
                updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeLeft)
                break
            case .portraitUpsideDown:

                isLandscapeLeft      = false
                isLandscapeRight     = false
                isPortrait           = false
                isPortraitUpsideDown = true
                updatePreviewLayer(layer: previewLayerConnection, orientation: .portraitUpsideDown)
                break
            default:

                updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait)

                break
            }
        }
    }
}

func fixOrientation(img: UIImage) -> UIImage {
        if (img.imageOrientation == .up) {
            return img
        }

        UIGraphicsBeginImageContextWithOptions(img.size, false, img.scale)
        let rect = CGRect(x: 0, y: 0, width: img.size.width, height: img.size.height)
        img.draw(in: rect)

        let normalizedImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        return normalizedImage
    }

从纵向设备创建图像时,您应该考虑 UIImage 已旋转 90 度,例如:

switch UIApplication.shared.statusBarOrientation {
case .portrait:
    image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .right)
case .landscapeRight:
    image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .up)
case .landscapeLeft:
    image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .down)
case .portraitUpsideDown:
    image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .left)
default:
    fatalError("Unexpected orientation")
}

有关完整示例,请参阅:

import UIKit
import AVFoundation

class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var videoPreviewView: UIView!
    var videoPreviewLayer: AVCaptureVideoPreviewLayer!

    var session: AVCaptureSession = AVCaptureSession()
    var stillImageOutput: AVCaptureStillImageOutput = {
        let output = AVCaptureStillImageOutput()
        output.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
        return output
    }()

    var images: [UIImage] = [UIImage]()

    var orientation = UIApplication.shared.statusBarOrientation

    override func viewDidLoad() {
        super.viewDidLoad()

        guard let device = AVCaptureDevice.default(for: .video) else {
            print("Unable to create capture device")
            return
        }

        let input: AVCaptureDeviceInput
        do {
            input = try AVCaptureDeviceInput(device: device)
        } catch {
            print("Unable to create input", error)
            return
        }

        guard session.canAddInput(input) else {
            print("Cannot add input")
            return
        }
        session.addInput(input)

        guard session.canAddOutput(stillImageOutput) else {
            print("Cannot add output")
            return
        }
        session.addOutput(stillImageOutput)

        session.startRunning()
        videoPreviewLayer = AVCaptureVideoPreviewLayer(session: session)
        videoPreviewView.layer.addSublayer(videoPreviewLayer)
    }

    @IBAction func didTapCaptureButton(_ sender: Any) {
        if let connection = stillImageOutput.connection(with: .video) {
            stillImageOutput.captureStillImageAsynchronously(from: connection) { sampleBuffer, error in
                guard let sampleBuffer = sampleBuffer, error == nil else {
                    print(error ?? "Unknown error")
                    return
                }

                guard
                    let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer),
                    let dataProvider = CGDataProvider(data: imageData as CFData),
                    let cgImageRef = CGImage(jpegDataProviderSource: dataProvider, decode: nil, shouldInterpolate: true, intent: .defaultIntent) else {
                        print("unable to capture image")
                        return
                }

                var image: UIImage?

                switch self.orientation {
                case .portrait:
                    image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .right)
                case .landscapeRight:
                    image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .up)
                case .landscapeLeft:
                    image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .down)
                case .portraitUpsideDown:
                    image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: .left)
                default:
                    fatalError("Unexpected orientation")
                }

                guard image != nil else {
                    print("unable to create UIImage")
                    return
                }

                DispatchQueue.main.async {
                    self.images.append(image!)
                    self.imageView.image = image
                }
            }

        }
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        if let connection =  self.videoPreviewLayer?.connection  {
            orientation = UIApplication.shared.statusBarOrientation

            switch orientation {
            case .portrait:           updatePreviewOrientation(for: connection, to: .portrait)
            case .landscapeRight:     updatePreviewOrientation(for: connection, to: .landscapeRight)
            case .landscapeLeft:      updatePreviewOrientation(for: connection, to: .landscapeLeft)
            case .portraitUpsideDown: updatePreviewOrientation(for: connection, to: .portraitUpsideDown)
            default:                  updatePreviewOrientation(for: connection, to: .portrait)
            }
        }
    }

    private func updatePreviewOrientation(for connection: AVCaptureConnection, to orientation: AVCaptureVideoOrientation) {
        if connection.isVideoOrientationSupported {
            connection.videoOrientation = orientation
        }
        videoPreviewLayer.frame = videoPreviewView.bounds
    }
}

您正在手动转换 fixOrientation 中的图像方向。使用适当的 orientationCGImage 创建 UIImage 后,如果不需要,您不必再重新创建图像。