fix: trace debug — show Stranger_NNN for unnamed traces instead of unknown

This commit is contained in:
Accusys
2026-05-14 15:12:21 +08:00
parent 8f013cbdbc
commit c90394897d
42 changed files with 1806 additions and 1722 deletions

View File

@@ -1 +1 @@
55053
17505

File diff suppressed because one or more lines are too long

View File

@@ -55,7 +55,7 @@
"LIBRARY_PATH" : "/usr/local/lib",
"LOGNAME" : "accusys",
"MANPATH" : "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/share/man:/Library/Developer/CommandLineTools/usr/share/man:/Library/Developer/CommandLineTools/Toolchains/XcodeDefault.xctoolchain/usr/share/man:",
"OLDPWD" : "/Users/accusys",
"OLDPWD" : "/Users/accusys/momentry_core_0.1/scripts/swift_processors",
"OPENCODE" : "1",
"OPENCODE_PID" : "1458",
"OPENCODE_PROCESS_ROLE" : "worker",
@@ -76,7 +76,7 @@
"__CFBundleIdentifier" : "com.googlecode.iterm2",
"__CF_USER_TEXT_ENCODING" : "0x1F5:0x0:0x0"
},
"inputHash" : "3faa9a4477d1e318c5f16d2c9fceb9eebe648921c6297d266ad3a15a6c49f202",
"inputHash" : "53f3a379d09ddfa90c5740d510d9e67e676b6f02a47252c63b128688222763ec",
"output" : "",
"result" : {
"exit" : {

View File

@@ -55,7 +55,7 @@
"LIBRARY_PATH" : "/usr/local/lib",
"LOGNAME" : "accusys",
"MANPATH" : "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/share/man:/Library/Developer/CommandLineTools/usr/share/man:/Library/Developer/CommandLineTools/Toolchains/XcodeDefault.xctoolchain/usr/share/man:",
"OLDPWD" : "/Users/accusys",
"OLDPWD" : "/Users/accusys/momentry_core_0.1/scripts/swift_processors",
"OPENCODE" : "1",
"OPENCODE_PID" : "1458",
"OPENCODE_PROCESS_ROLE" : "worker",
@@ -76,7 +76,7 @@
"__CFBundleIdentifier" : "com.googlecode.iterm2",
"__CF_USER_TEXT_ENCODING" : "0x1F5:0x0:0x0"
},
"inputHash" : "01434ad628ddb16c9ce2b957fc96867e99fa6e30223390316f069bf26c365418",
"inputHash" : "59cb90e0d1c12f3974d89b55cd41ec8eab762189e688dad1e4943d36a197def4",
"output" : "",
"result" : {
"exit" : {

View File

@@ -111,48 +111,62 @@ struct SwiftFace: ParsableCommand {
return
}
guard let faceObservations = detectReq.results, !faceObservations.isEmpty else {
let faceObservations = detectReq.results ?? []
let landmarkObservations = lmReq.results ?? []
guard !faceObservations.isEmpty || !landmarkObservations.isEmpty else {
return
}
let landmarkObservations = lmReq.results ?? []
let seconds = CMTimeGetSeconds(actualTime)
let frameNumber = Int(seconds * Double(fps))
var frameFaces: [[String: Any]] = []
for (idx, observation) in faceObservations.enumerated() {
let bb = observation.boundingBox
let faceX = Int(bb.origin.x * CGFloat(width))
let faceY = Int((1.0 - bb.origin.y - bb.size.height) * CGFloat(height))
let faceW = Int(bb.size.width * CGFloat(width))
let faceH = Int(bb.size.height * CGFloat(height))
// Use actual CGImage size (may differ from naturalSize after transform)
let imgW = CGFloat(cgImage.width)
let imgH = CGFloat(cgImage.height)
// Process landmark observations FIRST (each has bbox + landmarks, self-consistent)
for lmObs in landmarkObservations {
let bb = lmObs.boundingBox
let faceX = Int(bb.origin.x * imgW)
let faceY = Int((1.0 - bb.origin.y - bb.size.height) * imgH)
let faceW = Int(bb.size.width * imgW)
let faceH = Int(bb.size.height * imgH)
var faceData: [String: Any] = [
"bbox": ["x": max(0, faceX), "y": max(0, faceY),
"width": faceW, "height": faceH],
"confidence": Double(observation.faceCaptureQuality ?? observation.confidence),
"confidence": Double(lmObs.confidence),
]
if let yaw = observation.yaw?.doubleValue,
let roll = observation.roll?.doubleValue {
// Pose from landmark observation
if let yaw = lmObs.yaw?.doubleValue,
let roll = lmObs.roll?.doubleValue {
var poseInfo: [String: Any] = ["roll": roll, "yaw": yaw]
if let pitch = observation.pitch?.doubleValue {
if let pitch = lmObs.pitch?.doubleValue {
poseInfo["pitch"] = pitch
}
faceData["pose"] = poseInfo
}
if idx < landmarkObservations.count,
let lms = landmarkObservations[idx].landmarks {
let imgSize = CGSize(width: width, height: height)
// Landmarks with Y-flip (macOS image coords: bottom-left -> top-left)
if let lms = lmObs.landmarks {
let imgSize = CGSize(width: imgW, height: imgH)
let leftEye = lms.leftEye?.pointsInImage(imageSize: imgSize) ?? []
let rightEye = lms.rightEye?.pointsInImage(imageSize: imgSize) ?? []
let nose = lms.nose?.pointsInImage(imageSize: imgSize) ?? []
if !leftEye.isEmpty || !rightEye.isEmpty || !nose.isEmpty {
var lm: [String: [[Double]]] = [:]
if !leftEye.isEmpty { lm["left_eye"] = leftEye.map { [Double($0.x), Double($0.y)] } }
if !rightEye.isEmpty { lm["right_eye"] = rightEye.map { [Double($0.x), Double($0.y)] } }
if !nose.isEmpty { lm["nose"] = nose.map { [Double($0.x), Double($0.y)] } }
if !leftEye.isEmpty {
lm["left_eye"] = leftEye.map { [Double($0.x), Double(imgH - $0.y)] }
}
if !rightEye.isEmpty {
lm["right_eye"] = rightEye.map { [Double($0.x), Double(imgH - $0.y)] }
}
if !nose.isEmpty {
lm["nose"] = nose.map { [Double($0.x), Double(imgH - $0.y)] }
}
faceData["landmarks"] = lm
}
@@ -160,8 +174,8 @@ struct SwiftFace: ParsableCommand {
let inner = lms.innerLips?.pointsInImage(imageSize: imgSize) ?? []
if !outer.isEmpty || !inner.isEmpty {
faceData["lips"] = [
"outer_lips": outer.map { [Double($0.x), Double($0.y)] },
"inner_lips": inner.map { [Double($0.x), Double($0.y)] }
"outer_lips": outer.map { [Double($0.x), Double(imgH - $0.y)] },
"inner_lips": inner.map { [Double($0.x), Double(imgH - $0.y)] }
]
}
}
@@ -169,6 +183,48 @@ struct SwiftFace: ParsableCommand {
frameFaces.append(faceData)
}
// Output face rect observations that the landmark detector missed.
// Match against ALL landmark observations via IoU to avoid duplicates.
for faceObs in faceObservations {
let fBB = faceObs.boundingBox
var matched = false
for lmObs in landmarkObservations {
let lBB = lmObs.boundingBox
let ix = max(fBB.origin.x, lBB.origin.x)
let iy = max(fBB.origin.y, lBB.origin.y)
let iw = min(fBB.maxX, lBB.maxX) - ix
let ih = min(fBB.maxY, lBB.maxY) - iy
if iw <= 0 || ih <= 0 { continue }
let intersection = iw * ih
let union = fBB.width * fBB.height + lBB.width * lBB.height - intersection
if intersection / union > 0.3 {
matched = true
break
}
}
if matched { continue }
// Unmatched face rect: output without landmarks
let faceX = Int(fBB.origin.x * imgW)
let faceY = Int((1.0 - fBB.origin.y - fBB.size.height) * imgH)
let faceW = Int(fBB.size.width * imgW)
let faceH = Int(fBB.size.height * imgH)
var faceData: [String: Any] = [
"bbox": ["x": max(0, faceX), "y": max(0, faceY),
"width": faceW, "height": faceH],
"confidence": Double(faceObs.faceCaptureQuality ?? faceObs.confidence),
]
if let yaw = faceObs.yaw?.doubleValue,
let roll = faceObs.roll?.doubleValue {
var poseInfo: [String: Any] = ["roll": roll, "yaw": yaw]
if let pitch = faceObs.pitch?.doubleValue {
poseInfo["pitch"] = pitch
}
faceData["pose"] = poseInfo
}
frameFaces.append(faceData)
}
if !frameFaces.isEmpty {
allFrames.append([
"frame": frameNumber,

View File

@@ -270,19 +270,19 @@ def process_yolo(
# Load YOLO model (prefer CoreML for ANE acceleration, fallback to PyTorch)
model_path_mlpackage = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "..", "yolov5nu.mlpackage"
os.path.dirname(os.path.abspath(__file__)), "..", "yolov8s.mlpackage"
)
model_path_pt = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "..", "yolov5nu.pt"
os.path.dirname(os.path.abspath(__file__)), "..", "yolov8s.pt"
)
if os.path.exists(model_path_mlpackage):
model = YOLO(model_path_mlpackage)
print("YOLO: CoreML model loaded (4.5x ANE acceleration)")
print("YOLO: CoreML model loaded (YOLOv8s, ANE accelerated)")
elif os.path.exists(model_path_pt):
model = YOLO(model_path_pt)
print("YOLO: PyTorch model loaded")
print("YOLO: PyTorch model loaded (YOLOv8s)")
else:
model = YOLO("yolov5nu.pt") # will auto-download
model = YOLO("yolov8s.pt") # will auto-download
# Get video info
import cv2