package main import ( "context" "errors" "flag" "fmt" "io" "os" "os/exec" "time" "silverpose/v2/internal/fall" "silverpose/v2/internal/pose" "silverpose/v2/internal/spike" ) func main() { ffmpegPath := flag.String("ffmpeg", "ffmpeg", "path to ffmpeg.exe") videoPath := flag.String("video", "..\\demo\\1.mp4", "local non-sensitive validation video") width := flag.Int("width", 848, "source video frame width") height := flag.Int("height", 480, "source video frame height") fps := flag.Float64("fps", 30, "replay timestamp frames per second") maxFrames := flag.Int("max-frames", 0, "maximum frames to process; zero reads the complete video") modelPath := flag.String("onnx", "assets\\best.onnx", "locked ONNX pose model path") runtimeDLLPath := flag.String("ort-dll", "", "absolute onnxruntime.dll path") flag.Parse() if *runtimeDLLPath == "" || *width <= 0 || *height <= 0 || *fps <= 0 || *maxFrames < 0 { fail("--ort-dll is required; width, height and fps must be positive; max-frames cannot be negative") } engine, err := fall.NewEngine(fall.EngineConfig{ KeypointConfidenceThreshold: 0.4, SuspectWindowSeconds: 0.5, ConfirmWindowSeconds: 1.8, RecoveryWindowSeconds: 2.0, CooldownSeconds: 10.0, RequireRapidDrop: false, RequireLowerBody: false, HorizontalAngleThresholdDegrees: 45.0, ConfigVersion: "v1-default-20260722", SessionID: "video-regression", }) if err != nil { fail("create event engine: %v", err) } runtime, err := spike.OpenRuntime(*modelPath, *runtimeDLLPath) if err != nil { fail("open ONNX Pose runtime: %v", err) } defer runtime.Close() ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() command := exec.CommandContext(ctx, *ffmpegPath, "-v", "error", "-i", *videoPath, "-f", "rawvideo", "-pix_fmt", "bgr24", "-") stdout, err := command.StdoutPipe() if err != nil { fail("open FFmpeg output: %v", err) } if err := command.Start(); err != nil { fail("start FFmpeg: %v", err) } frameBytes := *width * *height * 3 frame := make([]byte, frameBytes) frameCount := 0 personFrames := 0 personCount := 0 events := make([]fall.Event, 0) limited := false for *maxFrames == 0 || frameCount < *maxFrames { _, err := io.ReadFull(stdout, frame) if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { break } if err != nil { fail("read FFmpeg BGR frame: %v", err) } input, transform, err := pose.PreprocessBGR(frame, *width, *height, 640) if err != nil { fail("preprocess frame %d: %v", frameCount, err) } output, err := runtime.Run(input) if err != nil { fail("run ONNX Pose for frame %d: %v", frameCount, err) } people, err := pose.ParseYOLOv8Pose(output, transform, 0.25, 0.70) if err != nil { fail("parse ONNX Pose for frame %d: %v", frameCount, err) } result := engine.Process(fall.Frame{Timestamp: float64(frameCount) / *fps, Width: *width, Height: *height, Poses: people}) if len(people) > 0 { personFrames++ personCount += len(people) } events = append(events, result.Events...) frameCount++ } if *maxFrames > 0 && frameCount == *maxFrames { limited = true cancel() } if err := command.Wait(); err != nil { if !limited { fail("FFmpeg ended with an error: %v", err) } } fmt.Printf("REGRESSION_OK frames=%d person_frames=%d people=%d events=%d\n", frameCount, personFrames, personCount, len(events)) for _, event := range events { fmt.Printf("EVENT id=%s track=%s latency=%.6f state=%s\n", event.EventID, event.TrackID, event.LatencySeconds, event.State) } } func fail(format string, args ...any) { fmt.Fprintf(os.Stderr, "regression: "+format+"\n", args...) os.Exit(1) }