拍摄照片时正确设置正确的照片方向

Posted

技术标签:

【中文标题】拍摄照片时正确设置正确的照片方向【英文标题】:Correctly set the right picture orientation when shooting photo 【发布时间】:2022-01-14 11:43:30 【问题描述】:

在我用 Swift 编写的 ios 应用上,我需要拍照并将它们保存在图库中;作为Apple文档,如果手机是纵向的,所有照片都是横向拍摄的;如果我们按原样保存图片,它将旋转 90° 保存。

问题:保存图片时如何正确管理设备方向?

感谢一些搜索,我使用了这个解决方案:

    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) 
        // ...error handling here

        guard let imageData = photo.fileDataRepresentation(with: self) else 
            NSLog("Fail to convert pixel buffer")
            return
        
        
        // Save image to gallery here
    

我的班级是AVCapturePhotoFileDataRepresentationCustomizer 代表,所以:

    func replacementMetadata(for photo: AVCapturePhoto) -> [String : Any]? 
        var properties = photo.metadata

        // Image orientation
        properties[kCGImagePropertyOrientation as String] = CGImagePropertyOrientation.right
        
        let exifData = NSMutableDictionary(dictionary: properties[kCGImagePropertyExifDictionary as String] as! NSDictionary)
        let xDimension = exifData[kCGImagePropertyExifPixelYDimension as String]
        let yDimension = exifData[kCGImagePropertyExifPixelXDimension as String]
        
        if xDimension != nil && yDimension != nil 
            exifData[kCGImagePropertyExifPixelXDimension as String] = xDimension
            exifData[kCGImagePropertyExifPixelYDimension as String] = yDimension
            
            properties[kCGImagePropertyExifDictionary as String] = exifData
        
        
        return properties
    

因为图像是纵向拍摄的,所以方向是 .right,我在搜索中读到 exif 数据中的 X 和 Y 尺寸也应该交换。

不幸的是,一切都没有结果:使用 exif 资源管理器图像方向检查保存的图像仍然是 0=未知值,X 和 Y 仍为原始设置。

我确定数据设置和写入正确,因为:

    断点return properties 强调方向标签设置正确;此外,如果标签未正确设置或未知,则应用程序崩溃...

    在同一个 replacementMetadata 函数中,我还设置了 GPS 数据(为了简单起见,我在这里剪掉了它!),我写了一些测试值(如标题 = 101),这些数据在最终图像元数据中正确报告。

所以我的问题仍然存在......感谢您使用代码 sn-ps 或文档为我指明正确的方向。

【问题讨论】:

【参考方案1】:

您可以通过设置videoOrientationAVCapturePhotoOutput 来将拍摄方向设置为您喜欢的任何方向。

为了与当前设备方向匹配,您可以使用UIDevice.current.orientation手动转换为AVCaptureVideoOrientation

let photoOutput = AVCapturePhotoOutput()

func takeShot() 

    // set whatever orientation you like
    let myShotOrientation = UIDevice.current.orientation.asCaptureVideoOrientation

    if let photoOutputConnection = self.photoOutput.connection(with: .video) 
        photoOutputConnection.videoOrientation = myShotOrientation
    

    photoOutput.capturePhoto(...)

UIDeviceOrientation 转换为AVCaptureVideoOrientation

extension UIDeviceOrientation 
    
    ///
    var asCaptureVideoOrientation: AVCaptureVideoOrientation 
        switch self 
        // YES, that's not a mistake
        case .landscapeLeft: return .landscapeRight
        case .landscapeRight: return .landscapeLeft
        case .portraitUpsideDown: return .portraitUpsideDown
        default: return .portrait
        
    

【讨论】:

【参考方案2】:
import Foundation
import CoreMotion
import AVFoundation
import UIKit

protocol OrientationHandlerDelegate: class 
   func didChange(deviceOrientation: UIDeviceOrientation)


class OrientationHandler 

  private let motionManager = CMMotionManager()
  private let queue = OperationQueue()
  private var deviceOrientation: UIDeviceOrientation = .unknown
  weak var delegate: OrientationHandlerDelegate?

  init() 
    motionManager.accelerometerUpdateInterval = 1.0
    motionManager.deviceMotionUpdateInterval = 1.0
    motionManager.gyroUpdateInterval = 1.0
    motionManager.magnetometerUpdateInterval = 1.0
  

  func startMeasuring() 
    guard motionManager.isDeviceMotionAvailable else 
        return
    
    motionManager.startAccelerometerUpdates(to: queue)  [weak self] (accelerometerData, error) in
        guard let strongSelf = self else 
            return
        
        guard let accelerometerData = accelerometerData else 
            return
        

        let acceleration = accelerometerData.acceleration
        let xx = -acceleration.x
        let yy = acceleration.y
        let z = acceleration.z
        let angle = atan2(yy, xx)
        var deviceOrientation = strongSelf.deviceOrientation
        let absoluteZ = fabs(z)

        if deviceOrientation == .faceUp || deviceOrientation == .faceDown 
            if absoluteZ < 0.845 
                if angle < -2.6 
                    deviceOrientation = .landscapeRight
                 else if angle > -2.05 && angle < -1.1 
                    deviceOrientation = .portrait
                 else if angle > -0.48 && angle < 0.48 
                    deviceOrientation = .landscapeLeft
                 else if angle > 1.08 && angle < 2.08 
                    deviceOrientation = .portraitUpsideDown
                
             else if z < 0 
                deviceOrientation = .faceUp
             else if z > 0 
                deviceOrientation = .faceDown
            
         else 
            if z > 0.875 
                deviceOrientation = .faceDown
             else if z < -0.875 
                deviceOrientation = .faceUp
             else 
                switch deviceOrientation 
                case .landscapeLeft:
                    if angle < -1.07 
                        deviceOrientation = .portrait
                    
                    if angle > 1.08 
                        deviceOrientation = .portraitUpsideDown
                    
                case .landscapeRight:
                    if angle < 0 && angle > -2.05 
                        deviceOrientation = .portrait
                    
                    if angle > 0 && angle < 2.05 
                        deviceOrientation = .portraitUpsideDown
                    
                case .portraitUpsideDown:
                    if angle > 2.66 
                        deviceOrientation = .landscapeRight
                    
                    if angle < 0.48 
                        deviceOrientation = .landscapeLeft
                    
                case .portrait:
                    if angle > -0.47 
                        deviceOrientation = .landscapeLeft
                    
                    if angle < -2.64 
                        deviceOrientation = .landscapeRight
                    
                default:
                    if angle > -0.47 
                        deviceOrientation = .landscapeLeft
                    
                    if angle < -2.64 
                        deviceOrientation = .landscapeRight
                    
                
            
        
        if strongSelf.deviceOrientation != deviceOrientation 
            strongSelf.deviceOrientation = deviceOrientation
            strongSelf.delegate?.didChange(deviceOrientation: deviceOrientation)
        
    
  

  func stopMeasuring() 
    motionManager.stopAccelerometerUpdates()
  

  func currentInterfaceOrientation() -> AVCaptureVideoOrientation 
    switch deviceOrientation 
    case .portrait:
        return .portrait
    case .landscapeRight:
        return .landscapeLeft
    case .landscapeLeft:
        return .landscapeRight
    case .portraitUpsideDown:
        return .portraitUpsideDown
    default:
        return .portrait
    
  

在您的文件中包含相机代码:

let orientationHandler = OrientationHandler()

当相机处于活动状态时开始测量方向。

orientationHandler.delegate = self
orientationHandler.startMeasuring()

不使用相机时停止测量方向。

orientationHandler.stopMeasuring()
orientationHandler.delegate = nil

最后在调用 capturePhoto 之前添加一行(with: settings, delegate: self)

//Need to correct image orientation before moving further
    if let photoOutputConnection = photoOutput?.connection(with: .video) 
        photoOutputConnection.videoOrientation = orientationHandler.currentInterfaceOrientation()
    
    photoOutput?.capturePhoto(with: settings, delegate: self)

【讨论】:

以上是关于拍摄照片时正确设置正确的照片方向的主要内容,如果未能解决你的问题,请参考以下文章

以纵向拍摄的照片正在以横向保存

IOS Swift 3:拍摄相机图片后水平翻转前置摄像头图像

设置 Android 照片 EXIF 方向

上传PHP后修复iOS图片方向

离子:每张照片/拍摄多张照片后,相机都会要求确认

Iphone上传的Jcrop方向不正确,我该怎么办?