Accept Vision single-precision angle boundaries in saved analysis

This commit is contained in:
Mikei386
2026-09-13 10:23:19 +02:00
parent 2efe6929d1
commit e1457aede1
3 changed files with 25 additions and 5 deletions
+2 -2
View File
@@ -19,9 +19,9 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.4.0</string> <string>0.4.1</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>5</string> <string>6</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>13.0</string> <string>13.0</string>
<key>NSHighResolutionCapable</key> <key>NSHighResolutionCapable</key>
+4 -3
View File
@@ -924,15 +924,16 @@ final class VideoProcessor: ObservableObject {
if let pose = sample.pose { if let pose = sample.pose {
guard [pose.x, pose.y, pose.height, pose.yaw, pose.roll].allSatisfy({ $0.isFinite }), guard [pose.x, pose.y, pose.height, pose.yaw, pose.roll].allSatisfy({ $0.isFinite }),
(0...1).contains(pose.x), (0...1).contains(pose.y), (0...1).contains(pose.height), (0...1).contains(pose.x), (0...1).contains(pose.y), (0...1).contains(pose.height),
abs(pose.yaw) <= .pi, abs(pose.roll) <= .pi, // Vision angles originate as Float; Float.pi is slightly larger than Double.pi.
pose.pitch == nil || (pose.pitch!.isFinite && abs(pose.pitch!) <= .pi) else { return false } abs(pose.yaw) <= Double(Float.pi), abs(pose.roll) <= Double(Float.pi),
pose.pitch == nil || (pose.pitch!.isFinite && abs(pose.pitch!) <= Double(Float.pi)) else { return false }
} }
if let color = sample.color { if let color = sample.color {
guard color.luminance.isFinite, (0...1).contains(color.luminance), guard color.luminance.isFinite, (0...1).contains(color.luminance),
[color.redToGreen, color.blueToGreen].compactMap({ $0 }).allSatisfy({ $0.isFinite && $0 > 0 && $0 < 10 }) else { return false } [color.redToGreen, color.blueToGreen].compactMap({ $0 }).allSatisfy({ $0.isFinite && $0 > 0 && $0 < 10 }) else { return false }
} }
return true return true
}) else { throw VideoProcessingError.invalidSettings("Ungültige gespeicherte Bildanalyse.") } }) else { throw VideoProcessingError.invalidSettings("Ungültige gespeicherte Bildanalyse: \(clip.displayName).") }
} }
for variant in clip.availableVariants { for variant in clip.availableVariants {
guard variant.sourcePath.hasPrefix("/"), variant.duration.isFinite, variant.duration > 0, guard variant.sourcePath.hasPrefix("/"), variant.duration.isFinite, variant.duration > 0,
+19
View File
@@ -196,6 +196,25 @@ struct RenderIntegration {
try require(loaded.loadProject(from: GrowthLapseProject.url(for: second.outputFileURL)) != nil, "Project reload") try require(loaded.loadProject(from: GrowthLapseProject.url(for: second.outputFileURL)) != nil, "Project reload")
print("PASS failure preservation, save-as, cache reuse and project reload") print("PASS failure preservation, save-as, cache reuse and project reload")
// Vision's single-precision ±pi must survive project validation unchanged.
var angleProject = second
let visionPi = Double(Float.pi)
angleProject.clips[0].visualAnalysis = ClipVisualAnalysis(sourceSignature: "angle-test", samples: [
VisualSample(time: 0, faceCount: 1,
pose: FacePose(x: 0.5, y: 0.5, height: 0.3, yaw: visionPi,
roll: -visionPi, eyesVisible: true, pitch: visionPi), color: nil, quality: 0)
])
let angleURL = root.appendingPathComponent("vision-angles.json")
try JSONEncoder().encode(angleProject).write(to: angleURL)
let angleLoader = VideoProcessor()
let accepted = angleLoader.loadProject(from: angleURL)
try require(accepted?.clips[0].visualAnalysis == angleProject.clips[0].visualAnalysis,
"Valid Vision Float.pi angles rejected or altered")
angleProject.clips[0].visualAnalysis!.samples[0].pose!.roll = 3.15
try JSONEncoder().encode(angleProject).write(to: angleURL)
try require(angleLoader.loadProject(from: angleURL) == nil, "Out-of-range angle accepted")
print("PASS Vision Float.pi project round-trip and invalid-angle rejection")
// Exercise failure after normalization, while FFmpeg is producing the final video. // Exercise failure after normalization, while FFmpeg is producing the final video.
let wrapper = root.appendingPathComponent("ffmpeg-fail") let wrapper = root.appendingPathComponent("ffmpeg-fail")
let quotedFFmpeg = "'" + tools.ffmpeg.path.replacingOccurrences(of: "'", with: "'\\''") + "'" let quotedFFmpeg = "'" + tools.ffmpeg.path.replacingOccurrences(of: "'", with: "'\\''") + "'"