如何编码 Swift 中的 simd_float4x4 元素数组(将 simd_float4x4 转换为数据)?
How can I encode an array of simd_float4x4 elements in Swift (convert simd_float4x4 to Data)?
我正在构建一个应用程序,用于从 iPhone TrueDepth 相机捕获面部跟踪数据。
我需要将此数据写入文件,以便我可以将其用作另一个应用程序的基础。
在应用程序中,数据被保存到四个单独的数组中,一个包含 ARFaceGeometry 对象,另外三个具有作为 simd_float4x4 矩阵的变换坐标。
我正在使用 archivedData(withRootObject: requiringSecureCoding:)
将数组转换为数据对象,然后调用 write(to:)
来创建文件。
包含 ARFaceGeometry 数据的文件被正确写入和读回。但是三个 simd_float4x4 数组并没有被写入,尽管这样做的代码是相同的。连同我的打印日志,给出的错误是 'unrecognized selector sent to instance'.
属性:
var faceGeometryCapture = [ARFaceGeometry]()
var faceTransformCapture = [simd_float4x4]()
var leftEyeTransformCapture = [simd_float4x4]()
var rightEyeTransformCapture = [simd_float4x4]()
var faceGeometryCaptureFilePath: URL!
var faceTransformCaptureFilePath: URL!
var leftEyeTransformCaptureFilePath: URL!
var rightEyeTransformCaptureFilePath: URL!
建立文件URL的代码:
let fileManager = FileManager.default
let dirPaths = fileManager.urls(for: .documentDirectory,
in: .userDomainMask)
faceGeometryCaptureFilePath = dirPaths[0].appendingPathComponent("face-geometries.txt")
faceTransformCaptureFilePath = dirPaths[0].appendingPathComponent("face-transforms.txt")
leftEyeTransformCaptureFilePath = dirPaths[0].appendingPathComponent("left-eye-transforms.txt")
rightEyeTransformCaptureFilePath = dirPaths[0].appendingPathComponent("right-eye-transforms.txt")
将数据写入文件的代码:
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: faceGeometryCapture, requiringSecureCoding: false)
try data.write(to: faceGeometryCaptureFilePath)
} catch { print("Error writing face geometries to file") }
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: faceTransformCapture, requiringSecureCoding: false)
try data.write(to: faceTransformCaptureFilePath)
} catch { print("Error writing face transforms to file") }
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: leftEyeTransformCapture, requiringSecureCoding: false)
try data.write(to: leftEyeTransformCaptureFilePath)
} catch { print("Error writing left eye transforms to file") }
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: rightEyeTransformCapture, requiringSecureCoding: false)
try data.write(to: rightEyeTransformCaptureFilePath)
} catch { print("Error writing right eye transforms to file") }
我猜是 simd_float4x4 结构导致了这个问题,因为这是工作和不工作之间的唯一区别。谁能确认并提出解决方案?
提前致谢。
正如评论中提到的,结构不符合 NSCoding 但你可以使 simd_float4x4 符合 Codable 并保留其数据:
extension simd_float4x4: Codable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
try self.init(container.decode([SIMD4<Float>].self))
}
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode([columns.0,columns.1, columns.2, columns.3])
}
}
游乐场测试:
do {
let vector = simd_float4x4(2.7) // simd_float4x4([[2.7, 0.0, 0.0, 0.0], [0.0, 2.7, 0.0, 0.0], [0.0, 0.0, 2.7, 0.0], [0.0, 0.0, 0.0, 2.7]])
let data = try JSONEncoder().encode(vector) // 111 bytes
let json = String(data: data, encoding: .utf8)
print(json ?? "") // [[[2.7000000476837158,0,0,0],[0,2.7000000476837158,0,0],[0,0,2.7000000476837158,0],[0,0,0,2.7000000476837158]]]\n"
let decoded = try JSONDecoder().decode(simd_float4x4.self, from: data)
print(decoded) // "simd_float4x4([[2.7, 0.0, 0.0, 0.0], [0.0, 2.7, 0.0, 0.0], [0.0, 0.0, 2.7, 0.0], [0.0, 0.0, 0.0, 2.7]])\n"
decoded == vector // true
} catch {
print(error)
}
edit/update:
另一种选择是保存其原始字节。它将仅使用 64 个字节:
extension simd_float4x4: ContiguousBytes {
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
try Swift.withUnsafeBytes(of: self) { try body([=12=]) }
}
}
extension ContiguousBytes {
init<T: ContiguousBytes>(_ bytes: T) {
self = bytes.withUnsafeBytes { [=13=].load(as: Self.self) }
}
var bytes: [UInt8] { withUnsafeBytes { .init([=13=]) } }
var data: Data { withUnsafeBytes { .init([=13=]) } }
func object<T>() -> T { withUnsafeBytes { [=13=].load(as: T.self) } }
func objects<T>() -> [T] { withUnsafeBytes { .init([=13=].bindMemory(to: T.self)) } }
var simdFloat4x4: simd_float4x4 { object() }
var simdFloat4x4Collection: [simd_float4x4] { objects() }
}
extension Array where Element: ContiguousBytes {
var bytes: [UInt8] { withUnsafeBytes { .init([=14=]) } }
var data: Data { withUnsafeBytes { .init([=14=]) } }
}
let vector1 = simd_float4x4(.init(2, 1, 1, 1), .init(1, 2, 1, 1), .init(1, 1, 2, 1), .init(1, 1, 1, 2))
let vector2 = simd_float4x4(.init(3, 1, 1, 1), .init(1, 3, 1, 1), .init(1, 1, 3, 1), .init(1, 1, 1, 3))
let data = [vector1,vector2].data // 128 bytes
let loaded = data.simdFloat4x4Collection
print(loaded) // "[simd_float4x4([[2.0, 1.0, 1.0, 1.0], [1.0, 2.0, 1.0, 1.0], [1.0, 1.0, 2.0, 1.0], [1.0, 1.0, 1.0, 2.0]]), simd_float4x4([[3.0, 1.0, 1.0, 1.0], [1.0, 3.0, 1.0, 1.0], [1.0, 1.0, 3.0, 1.0], [1.0, 1.0, 1.0, 3.0]])]\n"
loaded[0] == vector1 // true
loaded[1] == vector2 // true
我正在构建一个应用程序,用于从 iPhone TrueDepth 相机捕获面部跟踪数据。
我需要将此数据写入文件,以便我可以将其用作另一个应用程序的基础。
在应用程序中,数据被保存到四个单独的数组中,一个包含 ARFaceGeometry 对象,另外三个具有作为 simd_float4x4 矩阵的变换坐标。
我正在使用 archivedData(withRootObject: requiringSecureCoding:)
将数组转换为数据对象,然后调用 write(to:)
来创建文件。
包含 ARFaceGeometry 数据的文件被正确写入和读回。但是三个 simd_float4x4 数组并没有被写入,尽管这样做的代码是相同的。连同我的打印日志,给出的错误是 'unrecognized selector sent to instance'.
属性:
var faceGeometryCapture = [ARFaceGeometry]()
var faceTransformCapture = [simd_float4x4]()
var leftEyeTransformCapture = [simd_float4x4]()
var rightEyeTransformCapture = [simd_float4x4]()
var faceGeometryCaptureFilePath: URL!
var faceTransformCaptureFilePath: URL!
var leftEyeTransformCaptureFilePath: URL!
var rightEyeTransformCaptureFilePath: URL!
建立文件URL的代码:
let fileManager = FileManager.default
let dirPaths = fileManager.urls(for: .documentDirectory,
in: .userDomainMask)
faceGeometryCaptureFilePath = dirPaths[0].appendingPathComponent("face-geometries.txt")
faceTransformCaptureFilePath = dirPaths[0].appendingPathComponent("face-transforms.txt")
leftEyeTransformCaptureFilePath = dirPaths[0].appendingPathComponent("left-eye-transforms.txt")
rightEyeTransformCaptureFilePath = dirPaths[0].appendingPathComponent("right-eye-transforms.txt")
将数据写入文件的代码:
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: faceGeometryCapture, requiringSecureCoding: false)
try data.write(to: faceGeometryCaptureFilePath)
} catch { print("Error writing face geometries to file") }
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: faceTransformCapture, requiringSecureCoding: false)
try data.write(to: faceTransformCaptureFilePath)
} catch { print("Error writing face transforms to file") }
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: leftEyeTransformCapture, requiringSecureCoding: false)
try data.write(to: leftEyeTransformCaptureFilePath)
} catch { print("Error writing left eye transforms to file") }
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: rightEyeTransformCapture, requiringSecureCoding: false)
try data.write(to: rightEyeTransformCaptureFilePath)
} catch { print("Error writing right eye transforms to file") }
我猜是 simd_float4x4 结构导致了这个问题,因为这是工作和不工作之间的唯一区别。谁能确认并提出解决方案?
提前致谢。
正如评论中提到的,结构不符合 NSCoding 但你可以使 simd_float4x4 符合 Codable 并保留其数据:
extension simd_float4x4: Codable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
try self.init(container.decode([SIMD4<Float>].self))
}
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode([columns.0,columns.1, columns.2, columns.3])
}
}
游乐场测试:
do {
let vector = simd_float4x4(2.7) // simd_float4x4([[2.7, 0.0, 0.0, 0.0], [0.0, 2.7, 0.0, 0.0], [0.0, 0.0, 2.7, 0.0], [0.0, 0.0, 0.0, 2.7]])
let data = try JSONEncoder().encode(vector) // 111 bytes
let json = String(data: data, encoding: .utf8)
print(json ?? "") // [[[2.7000000476837158,0,0,0],[0,2.7000000476837158,0,0],[0,0,2.7000000476837158,0],[0,0,0,2.7000000476837158]]]\n"
let decoded = try JSONDecoder().decode(simd_float4x4.self, from: data)
print(decoded) // "simd_float4x4([[2.7, 0.0, 0.0, 0.0], [0.0, 2.7, 0.0, 0.0], [0.0, 0.0, 2.7, 0.0], [0.0, 0.0, 0.0, 2.7]])\n"
decoded == vector // true
} catch {
print(error)
}
edit/update:
另一种选择是保存其原始字节。它将仅使用 64 个字节:
extension simd_float4x4: ContiguousBytes {
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
try Swift.withUnsafeBytes(of: self) { try body([=12=]) }
}
}
extension ContiguousBytes {
init<T: ContiguousBytes>(_ bytes: T) {
self = bytes.withUnsafeBytes { [=13=].load(as: Self.self) }
}
var bytes: [UInt8] { withUnsafeBytes { .init([=13=]) } }
var data: Data { withUnsafeBytes { .init([=13=]) } }
func object<T>() -> T { withUnsafeBytes { [=13=].load(as: T.self) } }
func objects<T>() -> [T] { withUnsafeBytes { .init([=13=].bindMemory(to: T.self)) } }
var simdFloat4x4: simd_float4x4 { object() }
var simdFloat4x4Collection: [simd_float4x4] { objects() }
}
extension Array where Element: ContiguousBytes {
var bytes: [UInt8] { withUnsafeBytes { .init([=14=]) } }
var data: Data { withUnsafeBytes { .init([=14=]) } }
}
let vector1 = simd_float4x4(.init(2, 1, 1, 1), .init(1, 2, 1, 1), .init(1, 1, 2, 1), .init(1, 1, 1, 2))
let vector2 = simd_float4x4(.init(3, 1, 1, 1), .init(1, 3, 1, 1), .init(1, 1, 3, 1), .init(1, 1, 1, 3))
let data = [vector1,vector2].data // 128 bytes
let loaded = data.simdFloat4x4Collection
print(loaded) // "[simd_float4x4([[2.0, 1.0, 1.0, 1.0], [1.0, 2.0, 1.0, 1.0], [1.0, 1.0, 2.0, 1.0], [1.0, 1.0, 1.0, 2.0]]), simd_float4x4([[3.0, 1.0, 1.0, 1.0], [1.0, 3.0, 1.0, 1.0], [1.0, 1.0, 3.0, 1.0], [1.0, 1.0, 1.0, 3.0]])]\n"
loaded[0] == vector1 // true
loaded[1] == vector2 // true