如何使用 AVCapturePhoto 包含位置数据?
How do I include location data using AVCapturePhoto?
我正在尝试在 Swift 中编写一个 iOS 应用程序,让用户可以拍照,然后我将在图像上叠加一些额外的数据。我希望图像包含位置数据。我正在使用 AVCapturePhoto,我可以在文档中看到它有一些元数据变量,但我找不到有关如何使用它们的任何信息。当我现在用我的应用拍照时,EXIF 信息中没有位置数据。
如何设置捕获会话以嵌入位置数据?
我还没有处理新的 IOS11 AVCapturePhoto 对象,所以这个答案必须对如何访问数据做出一些假设,但理论上所有这些都应该有效。
在添加位置数据之前,您需要询问用户是否可以使用他们的位置。将 "Privacy - Location When In Use" 标签添加到您的 Info.plist。然后在初始化的某处添加以下代码。
// Request authorization for location manager
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
break
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
locationManagerStatus = CLLocationManager.authorizationStatus()
default:
locationManagerStatus = .denied
}
下面将创建一个包含位置信息的字典。
// create GPS metadata properties
func createLocationMetadata() -> NSMutableDictionary? {
guard CLLocationManager.authorizationStatus() == .authorizedWhenInUse else {return nil}
if let location = locationManager.location {
let gpsDictionary = NSMutableDictionary()
var latitude = location.coordinate.latitude
var longitude = location.coordinate.longitude
var altitude = location.altitude
var latitudeRef = "N"
var longitudeRef = "E"
var altitudeRef = 0
if latitude < 0.0 {
latitude = -latitude
latitudeRef = "S"
}
if longitude < 0.0 {
longitude = -longitude
longitudeRef = "W"
}
if altitude < 0.0 {
altitude = -altitude
altitudeRef = 1
}
let formatter = DateFormatter()
formatter.dateFormat = "yyyy:MM:dd"
gpsDictionary[kCGImagePropertyGPSDateStamp] = formatter.string(from:location.timestamp)
formatter.dateFormat = "HH:mm:ss"
gpsDictionary[kCGImagePropertyGPSTimeStamp] = formatter.string(from:location.timestamp)
gpsDictionary[kCGImagePropertyGPSLatitudeRef] = latitudeRef
gpsDictionary[kCGImagePropertyGPSLatitude] = latitude
gpsDictionary[kCGImagePropertyGPSLongitudeRef] = longitudeRef
gpsDictionary[kCGImagePropertyGPSLongitude] = longitude
gpsDictionary[kCGImagePropertyGPSDOP] = location.horizontalAccuracy
gpsDictionary[kCGImagePropertyGPSAltitudeRef] = altitudeRef
gpsDictionary[kCGImagePropertyGPSAltitude] = altitude
if let heading = locationManager.heading {
gpsDictionary[kCGImagePropertyGPSImgDirectionRef] = "T"
gpsDictionary[kCGImagePropertyGPSImgDirection] = heading.trueHeading
}
return gpsDictionary;
}
return nil
}
这是我必须做一些猜测的地方,因为我还没有处理 IOS11 AVPhotoCapture。您将需要图像数据的文件数据表示。我假设
AVCapturePhoto.fileDataRepresentation()
returns这个。此外,您还需要原始文件元数据。我猜
AVCapturePhoto.metadata
包含这个。根据这些假设,以下函数将为您提供带有附加位置数据的文件数据表示。可能有更新的 IOS 11 种方法可以更简洁地执行此操作。
func getFileRepresentationWithLocationData(photo : AVCapturePhoto) -> Data {
// get image metadata
var properties = photo.metadata
// add gps data to metadata
if let gpsDictionary = createLocationMetadata() {
properties[kCGImagePropertyGPSDictionary as String] = gpsDictionary
}
// create new file representation with edited metadata
return photo.fileDataRepresentation(withReplacementMetadata:properties,
replacementEmbeddedThumbnailPhotoFormat:photo.embeddedThumbnailPhotoFormat,
replacementEmbeddedThumbnailPixelBuffer:photo.previewPixelBuffer,
replacementDepthData:photo.depthData)
}
@adamfowlerphoto 给出了迄今为止我能找到的关于这个主题的最佳答案!
AVCapturePhoto.fileDataRepresentation(withReplacementMetadata:replacementEmbeddedThumbnailPhotoFormat:replacementEmbeddedThumbnailPixelBuffer:replacementDepthData:)
在 iOS 12 中已弃用。对于新的 API,您必须使用方法 replacementMetadata()
实现 AVCapturePhotoFileDataRepresentationCustomizer
协议。这是更新后的代码:
extension CameraViewController: AVCapturePhotoFileDataRepresentationCustomizer {
// create GPS metadata properties
func createLocationMetadata() -> NSMutableDictionary? {
if let location = locationManager.location {
let gpsDictionary = NSMutableDictionary()
var latitude = location.coordinate.latitude
var longitude = location.coordinate.longitude
var altitude = location.altitude
var latitudeRef = "N"
var longitudeRef = "E"
var altitudeRef = 0
if latitude < 0.0 {
latitude = -latitude
latitudeRef = "S"
}
if longitude < 0.0 {
longitude = -longitude
longitudeRef = "W"
}
if altitude < 0.0 {
altitude = -altitude
altitudeRef = 1
}
let formatter = DateFormatter()
formatter.dateFormat = "yyyy:MM:dd"
gpsDictionary[kCGImagePropertyGPSDateStamp] = formatter.string(from:location.timestamp)
formatter.dateFormat = "HH:mm:ss"
gpsDictionary[kCGImagePropertyGPSTimeStamp] = formatter.string(from:location.timestamp)
gpsDictionary[kCGImagePropertyGPSLatitudeRef] = latitudeRef
gpsDictionary[kCGImagePropertyGPSLatitude] = latitude
gpsDictionary[kCGImagePropertyGPSLongitudeRef] = longitudeRef
gpsDictionary[kCGImagePropertyGPSLongitude] = longitude
gpsDictionary[kCGImagePropertyGPSDOP] = location.horizontalAccuracy
gpsDictionary[kCGImagePropertyGPSAltitudeRef] = altitudeRef
gpsDictionary[kCGImagePropertyGPSAltitude] = altitude
if let heading = locationManager.heading {
gpsDictionary[kCGImagePropertyGPSImgDirectionRef] = "T"
gpsDictionary[kCGImagePropertyGPSImgDirection] = heading.trueHeading
}
return gpsDictionary;
}
return nil
}
func getFileRepresentationWithLocationData(photo : AVCapturePhoto) -> Data {
// get image metadata
var properties = photo.metadata
// add gps data to metadata
if let gpsDictionary = createLocationMetadata() {
properties[kCGImagePropertyGPSDictionary as String] = gpsDictionary
}
// create new file representation with edited metadata
return photo.fileDataRepresentation(with: self) ?? Data()
}
func replacementMetadata(for photo: AVCapturePhoto) -> [String : Any]? {
var properties = photo.metadata
// add gps data to metadata
if let gpsDictionary = createLocationMetadata() {
properties[kCGImagePropertyGPSDictionary as String] = gpsDictionary
}
return properties
}
}
要打印结果,您可以使用:
static func printEXIFData(imageData: Data) {
var exifData: CFDictionary? = nil
imageData.withUnsafeBytes {
let bytes = [=11=].baseAddress?.assumingMemoryBound(to: UInt8.self)
if let cfData = CFDataCreate(kCFAllocatorDefault, bytes, imageData.count),
let source = CGImageSourceCreateWithData(cfData, nil) {
exifData = CGImageSourceCopyPropertiesAtIndex(source, 0, nil)
print(exifData)
}
}
}
不要尝试将 imageData 转换为 JPEG 表示,因为它会丢弃 GPS 元数据。 (来源:)
let data = jpegData(compressionQuality: 1.0) // NOOOOO
我正在尝试在 Swift 中编写一个 iOS 应用程序,让用户可以拍照,然后我将在图像上叠加一些额外的数据。我希望图像包含位置数据。我正在使用 AVCapturePhoto,我可以在文档中看到它有一些元数据变量,但我找不到有关如何使用它们的任何信息。当我现在用我的应用拍照时,EXIF 信息中没有位置数据。
如何设置捕获会话以嵌入位置数据?
我还没有处理新的 IOS11 AVCapturePhoto 对象,所以这个答案必须对如何访问数据做出一些假设,但理论上所有这些都应该有效。
在添加位置数据之前,您需要询问用户是否可以使用他们的位置。将 "Privacy - Location When In Use" 标签添加到您的 Info.plist。然后在初始化的某处添加以下代码。
// Request authorization for location manager
switch CLLocationManager.authorizationStatus() {
case .authorizedWhenInUse:
break
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
locationManagerStatus = CLLocationManager.authorizationStatus()
default:
locationManagerStatus = .denied
}
下面将创建一个包含位置信息的字典。
// create GPS metadata properties
func createLocationMetadata() -> NSMutableDictionary? {
guard CLLocationManager.authorizationStatus() == .authorizedWhenInUse else {return nil}
if let location = locationManager.location {
let gpsDictionary = NSMutableDictionary()
var latitude = location.coordinate.latitude
var longitude = location.coordinate.longitude
var altitude = location.altitude
var latitudeRef = "N"
var longitudeRef = "E"
var altitudeRef = 0
if latitude < 0.0 {
latitude = -latitude
latitudeRef = "S"
}
if longitude < 0.0 {
longitude = -longitude
longitudeRef = "W"
}
if altitude < 0.0 {
altitude = -altitude
altitudeRef = 1
}
let formatter = DateFormatter()
formatter.dateFormat = "yyyy:MM:dd"
gpsDictionary[kCGImagePropertyGPSDateStamp] = formatter.string(from:location.timestamp)
formatter.dateFormat = "HH:mm:ss"
gpsDictionary[kCGImagePropertyGPSTimeStamp] = formatter.string(from:location.timestamp)
gpsDictionary[kCGImagePropertyGPSLatitudeRef] = latitudeRef
gpsDictionary[kCGImagePropertyGPSLatitude] = latitude
gpsDictionary[kCGImagePropertyGPSLongitudeRef] = longitudeRef
gpsDictionary[kCGImagePropertyGPSLongitude] = longitude
gpsDictionary[kCGImagePropertyGPSDOP] = location.horizontalAccuracy
gpsDictionary[kCGImagePropertyGPSAltitudeRef] = altitudeRef
gpsDictionary[kCGImagePropertyGPSAltitude] = altitude
if let heading = locationManager.heading {
gpsDictionary[kCGImagePropertyGPSImgDirectionRef] = "T"
gpsDictionary[kCGImagePropertyGPSImgDirection] = heading.trueHeading
}
return gpsDictionary;
}
return nil
}
这是我必须做一些猜测的地方,因为我还没有处理 IOS11 AVPhotoCapture。您将需要图像数据的文件数据表示。我假设
AVCapturePhoto.fileDataRepresentation()
returns这个。此外,您还需要原始文件元数据。我猜
AVCapturePhoto.metadata
包含这个。根据这些假设,以下函数将为您提供带有附加位置数据的文件数据表示。可能有更新的 IOS 11 种方法可以更简洁地执行此操作。
func getFileRepresentationWithLocationData(photo : AVCapturePhoto) -> Data {
// get image metadata
var properties = photo.metadata
// add gps data to metadata
if let gpsDictionary = createLocationMetadata() {
properties[kCGImagePropertyGPSDictionary as String] = gpsDictionary
}
// create new file representation with edited metadata
return photo.fileDataRepresentation(withReplacementMetadata:properties,
replacementEmbeddedThumbnailPhotoFormat:photo.embeddedThumbnailPhotoFormat,
replacementEmbeddedThumbnailPixelBuffer:photo.previewPixelBuffer,
replacementDepthData:photo.depthData)
}
@adamfowlerphoto 给出了迄今为止我能找到的关于这个主题的最佳答案!
AVCapturePhoto.fileDataRepresentation(withReplacementMetadata:replacementEmbeddedThumbnailPhotoFormat:replacementEmbeddedThumbnailPixelBuffer:replacementDepthData:)
在 iOS 12 中已弃用。对于新的 API,您必须使用方法 replacementMetadata()
实现 AVCapturePhotoFileDataRepresentationCustomizer
协议。这是更新后的代码:
extension CameraViewController: AVCapturePhotoFileDataRepresentationCustomizer {
// create GPS metadata properties
func createLocationMetadata() -> NSMutableDictionary? {
if let location = locationManager.location {
let gpsDictionary = NSMutableDictionary()
var latitude = location.coordinate.latitude
var longitude = location.coordinate.longitude
var altitude = location.altitude
var latitudeRef = "N"
var longitudeRef = "E"
var altitudeRef = 0
if latitude < 0.0 {
latitude = -latitude
latitudeRef = "S"
}
if longitude < 0.0 {
longitude = -longitude
longitudeRef = "W"
}
if altitude < 0.0 {
altitude = -altitude
altitudeRef = 1
}
let formatter = DateFormatter()
formatter.dateFormat = "yyyy:MM:dd"
gpsDictionary[kCGImagePropertyGPSDateStamp] = formatter.string(from:location.timestamp)
formatter.dateFormat = "HH:mm:ss"
gpsDictionary[kCGImagePropertyGPSTimeStamp] = formatter.string(from:location.timestamp)
gpsDictionary[kCGImagePropertyGPSLatitudeRef] = latitudeRef
gpsDictionary[kCGImagePropertyGPSLatitude] = latitude
gpsDictionary[kCGImagePropertyGPSLongitudeRef] = longitudeRef
gpsDictionary[kCGImagePropertyGPSLongitude] = longitude
gpsDictionary[kCGImagePropertyGPSDOP] = location.horizontalAccuracy
gpsDictionary[kCGImagePropertyGPSAltitudeRef] = altitudeRef
gpsDictionary[kCGImagePropertyGPSAltitude] = altitude
if let heading = locationManager.heading {
gpsDictionary[kCGImagePropertyGPSImgDirectionRef] = "T"
gpsDictionary[kCGImagePropertyGPSImgDirection] = heading.trueHeading
}
return gpsDictionary;
}
return nil
}
func getFileRepresentationWithLocationData(photo : AVCapturePhoto) -> Data {
// get image metadata
var properties = photo.metadata
// add gps data to metadata
if let gpsDictionary = createLocationMetadata() {
properties[kCGImagePropertyGPSDictionary as String] = gpsDictionary
}
// create new file representation with edited metadata
return photo.fileDataRepresentation(with: self) ?? Data()
}
func replacementMetadata(for photo: AVCapturePhoto) -> [String : Any]? {
var properties = photo.metadata
// add gps data to metadata
if let gpsDictionary = createLocationMetadata() {
properties[kCGImagePropertyGPSDictionary as String] = gpsDictionary
}
return properties
}
}
要打印结果,您可以使用:
static func printEXIFData(imageData: Data) {
var exifData: CFDictionary? = nil
imageData.withUnsafeBytes {
let bytes = [=11=].baseAddress?.assumingMemoryBound(to: UInt8.self)
if let cfData = CFDataCreate(kCFAllocatorDefault, bytes, imageData.count),
let source = CGImageSourceCreateWithData(cfData, nil) {
exifData = CGImageSourceCopyPropertiesAtIndex(source, 0, nil)
print(exifData)
}
}
}
不要尝试将 imageData 转换为 JPEG 表示,因为它会丢弃 GPS 元数据。 (来源:)
let data = jpegData(compressionQuality: 1.0) // NOOOOO