feat: Swift Face Pose integration + TKG 方案 B
Major Changes: - swift_face_pose: output pose angles (yaw/pitch/roll) in face.json - face_processor.py: call swift_face_pose (dual output: face.json + pose.json) - Face struct: add pose_angle field - TKG 方案 B: gaze/lip_track nodes from face.json (no face_detections dependency) - Chunk cleanup: delete old data before rebuild (avoid duplicate key) - Hand nodes: classify by hand_type + gesture (15 combinations) - HAND_OBJECT edges: bbox spatial matching (174 matches) Test Results: - Blake Jones: 8 faces, pose_angle ✓, 66 nodes, 174 edges - FilmRiot: 394 faces, pose_angle ✓, 35 nodes, 39 edges - Left hands: 132, Right hands: 2 Architecture: - All TKG nodes built from JSON files (face.json, hand.json, yolo.json) - Swift processors: sample_interval=3 (Face/Pose/Hand sync) - Cleanup functions: delete_tkg_nodes_by_uuid, delete_tkg_edges_by_uuid
This commit is contained in:
@@ -118,5 +118,13 @@ let package = Package(
|
||||
path: ".",
|
||||
sources: ["swift_hand.swift"]
|
||||
),
|
||||
.executableTarget(
|
||||
name: "swift_face_pose",
|
||||
dependencies: [
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
],
|
||||
path: ".",
|
||||
sources: ["swift_face_pose.swift"]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -13,8 +13,8 @@ struct SwiftFace: ParsableCommand {
|
||||
@Argument(help: "Output JSON path")
|
||||
var outputPath: String
|
||||
|
||||
@Option(name: .long, help: "Sample interval (frames, default=30)")
|
||||
var sampleInterval: Int = 30
|
||||
@Option(name: .long, help: "Sample interval (frames, default=3)")
|
||||
var sampleInterval: Int = 3
|
||||
|
||||
@Option(name: .long, help: "UUID for logging")
|
||||
var uuid: String = ""
|
||||
|
||||
@@ -318,8 +318,18 @@ struct SwiftFacePose: ParsableCommand {
|
||||
"fps": Double(fps),
|
||||
"frames": faceFrames,
|
||||
]
|
||||
if let faceJson = try? JSONSerialization.data(withJSONObject: faceOutputDict, options: []) {
|
||||
do {
|
||||
let faceJson = try JSONSerialization.data(withJSONObject: faceOutputDict, options: [])
|
||||
try faceJson.write(to: URL(fileURLWithPath: faceOutput))
|
||||
print("[SwiftFacePose] Face output written: \(faceOutput)")
|
||||
// Verify file exists
|
||||
if FileManager.default.fileExists(atPath: faceOutput) {
|
||||
print("[SwiftFacePose] Verified: file exists at \(faceOutput)")
|
||||
} else {
|
||||
print("[SwiftFacePose] ERROR: file not found after write!")
|
||||
}
|
||||
} catch {
|
||||
print("[SwiftFacePose] ERROR writing face output: \(error)")
|
||||
}
|
||||
|
||||
let poseOutputDict: [String: Any] = [
|
||||
|
||||
@@ -18,7 +18,7 @@ struct SwiftHandProcessor: ParsableCommand {
|
||||
var uuid: String = ""
|
||||
|
||||
@Option(name: [.short, .long], help: "Sample interval (frames)")
|
||||
var sampleInterval: Int = 30
|
||||
var sampleInterval: Int = 3
|
||||
|
||||
@Option(name: [.long], help: "Minimum confidence threshold")
|
||||
var minConfidence: Double = 0.3
|
||||
|
||||
@@ -26,8 +26,8 @@ struct SwiftPose: ParsableCommand {
|
||||
@Argument(help: "Output JSON path")
|
||||
var outputPath: String
|
||||
|
||||
@Option(name: .long, help: "Sample interval (frames, default=30)")
|
||||
var sampleInterval: Int = 30
|
||||
@Option(name: .long, help: "Sample interval (frames, default=3)")
|
||||
var sampleInterval: Int = 3
|
||||
|
||||
@Option(name: .long, help: "UUID for logging")
|
||||
var uuid: String = ""
|
||||
|
||||
Reference in New Issue
Block a user