【问题标题】: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 仍为原始设置。

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

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

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

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

【问题讨论】:

    标签: ios swift exif phphotolibrary


    【解决方案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)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-11
        • 2014-05-22
        • 1970-01-01
        • 2011-03-30
        • 2012-06-11
        • 2019-04-11
        相关资源
        最近更新 更多