拍摄照片时正确设置正确的图片方向
Correctly set the right picture orientation when shooting photo
在我用 Swift 编写的 iOS 应用程序上,我需要拍照并将它们保存在图库中;作为 Apple 文档,如果 phone 是纵向的,所有照片也是横向拍摄的;如果我们按原样保存图片,它将旋转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
}
我的 class 是 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 为最初设置。
我确定数据已正确设置和写入,因为:
Breakpoing on return properties
强调方向标签设置正确;此外,如果标签设置不正确或未知,应用程序会崩溃...
在同一个 replacementMetadata
函数中,我还设置了 GPS 数据(为了简单起见,我在这里删掉了它!)并且我写了一些测试值(比如 heading = 101),这些数据是正确的报告了最终图像元数据。
所以我的问题仍然存在...感谢您用代码片段或文档为我指明正确的方向。
您可以通过设置 AVCapturePhotoOutput
的 videoOrientation
将您的拍摄方向设置为任何您喜欢的方向。
为了与当前设备方向匹配,您可以使用 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
}
}
}
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)
在我用 Swift 编写的 iOS 应用程序上,我需要拍照并将它们保存在图库中;作为 Apple 文档,如果 phone 是纵向的,所有照片也是横向拍摄的;如果我们按原样保存图片,它将旋转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
}
我的 class 是 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 为最初设置。
我确定数据已正确设置和写入,因为:
Breakpoing on
return properties
强调方向标签设置正确;此外,如果标签设置不正确或未知,应用程序会崩溃...在同一个
replacementMetadata
函数中,我还设置了 GPS 数据(为了简单起见,我在这里删掉了它!)并且我写了一些测试值(比如 heading = 101),这些数据是正确的报告了最终图像元数据。
所以我的问题仍然存在...感谢您用代码片段或文档为我指明正确的方向。
您可以通过设置 AVCapturePhotoOutput
的 videoOrientation
将您的拍摄方向设置为任何您喜欢的方向。
为了与当前设备方向匹配,您可以使用 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
}
}
}
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)