Detector-agnostic tracking and spatial event analytics in MoonBit
Dependencies
moon add python123-ops/mbmot@0.3.0moon fmt --check
moon check --target all --deny-warn
moon test --target all --deny-warnlet tracker = @mbmot.Tracker::new(@mbmot.TrackerConfig::default())
let box = @mbmot.BoundingBox::new(12.0, 20.0, 52.0, 80.0)
let detection = @mbmot.Detection::new(box, 0.91, 0)
let frame = tracker.update(1, [detection])
let track = frame.tracks()[0]
assert_eq(track.track_id(), 1)
assert_eq(track.hits(), 1)moon run src/yolo_import --target native < examples/yolo.ndjson
moon run src/yolo_import --target native -- --classes '[2]' < examples/yolo.ndjsonmoon run src/observe --target native -- examples/observe.rules.json < examples/observe.yolo.ndjsonmoon run src/observe --target native -- examples/observe.rules.json --input xyxy < examples/observe.xyxy.ndjson| 参数 | 值 | 作用 |
|---|---|---|
| high_score_threshold | 0.25 | 第一阶段检测下限 |
| low_score_threshold | 0.10 | 第二阶段检测下限 |
| new_track_threshold | 0.25 | 新建身份的最低分数 |
| first_match_max_cost | 0.80 | 高分关联最大代价 |
| second_match_max_cost | 0.50 | 低分关联最大代价 |
| max_lost_frames | 30 | lost 身份的保留帧数 |
| min_hits | 1 | 轨迹可见前所需命中数 |
| fuse_score | true | 第一阶段是否融合检测分数 |
let analyzer = @analytics.SpatialAnalyzer::new(
@analytics.AnalyticsConfig::default(),
[
@analytics.DirectedLine::new(
4,
@analytics.Point::new(320.0, 0.0),
@analytics.Point::new(320.0, 720.0),
),
],
[
@analytics.PolygonRegion::new(
7,
[
@analytics.Point::new(100.0, 100.0),
@analytics.Point::new(540.0, 100.0),
@analytics.Point::new(540.0, 620.0),
@analytics.Point::new(100.0, 620.0),
],
),
],
)
let tracked = tracker.update(frame_id, detections)
let spatial = analyzer.update_mbmot(frame_id, tracked)
for event in spatial.events() {
// LineCrossed / RegionEntered / RegionExited
consume(event)
}let selected_line = @analytics.DirectedLine::for_classes(
12,
@analytics.Point::new(320.0, 0.0),
@analytics.Point::new(320.0, 720.0),
[0, 2],
)moon run src/analytics_demo --target native{"config":{"anchor":"bottom_center","stable_frames":1},"lines":[{"id":4,"start":[5,0],"end":[5,10],"class_ids":[0]}],"regions":[{"id":7,"vertices":[[0,0],[10,0],[10,10],[0,10]],"class_ids":[0]}]}
{"frame":1,"tracks":[{"track_id":1,"xyxy":[-3,2,-1,5],"class_id":0}],"lost":[],"removed":[]}moon run src/analytics_replay --target native < examples/analytics.ndjsonpy -3 -m venv .venv-video-demo
.\.venv-video-demo\Scripts\python.exe -m pip install -r tools\video_demo\requirements.txt
.\.venv-video-demo\Scripts\python.exe tools\video_demo\video_demo.py `
--source input.mp4 `
--output input.mbmot.mp4 `
--config examples\video-demo-config.json.\.venv-video-demo\Scripts\python.exe tools\video_demo\video_demo.py `
--source 0 `
--max-frames 300 `
--output camera.mbmot.mp4 `
--config examples\video-demo-config.json{"frame":1,"detections":[{"xyxy":[120,80,260,410],"score":0.91,"class_id":0}]}
{"frame":1,"detections":[{"xyxy":[0,0,10,10],"score":0.95,"class_id":0}]}moon run src/replay --target native < examples/replay.ndjsoncmd /c "moon run src/replay --target native < examples\replay.ndjson"{"frame":1,"tracks":[{"track_id":1,"xyxy":[0,0,10,10],"score":0.95,"class_id":0,"first_frame":1,"last_frame":1,"hits":1}],"lost":[],"removed":[]}moon run src/evaluate --target native examples/mot/ground-truth.txt examples/mot/tracker-results.txtmoon run src/mot_track --target native --release \
examples/mot/detections.txt tracks.txt{"match_iou_threshold":0.5,"frames":3,"ground_truth_detections":3,"tracker_detections":4,"true_positives":3,"false_positives":1,"false_negatives":0,"identity_switches":1,"mota":0.3333333333333333,"motp":1,"id_true_positives":2,"id_false_positives":2,"id_false_negatives":1,"id_precision":0.5,"id_recall":0.6666666666666666,"idf1":0.5714285714285714}moon run src/mot_batch --target native -- examples/mot/batch/manifest.jsonpub(all) suberror TrackerError {
InvalidBoundingBox(String)
InvalidScore(Double)
InvalidClassId(Int)
InvalidConfig(String)
NonIncreasingFrame(previous~ : Int, received~ : Int)
InvalidDetection(index~ : Int)
} derive(Debug)fn BoundingBox::from_cxcywh(center_x : Double, center_y : Double, width : Double, height : Double) -> BoundingBox raise TrackerErrorfn BoundingBox::from_xywh(x : Double, y : Double, width : Double, height : Double) -> BoundingBox raise TrackerErrorfn BoundingBox::new(x1 : Double, y1 : Double, x2 : Double, y2 : Double) -> BoundingBox raise TrackerErrorfn Detection::new(bbox : BoundingBox, score : Double, class_id : Int) -> Detection raise TrackerErrorpub struct Track {
track_id : Int
bbox : BoundingBox
class_id : Int
score : Double
first_frame : Int
last_frame : Int
hits : Int
} derive(Eq, Debug)type Trackerfn Tracker::update(self : Tracker, frame_id : Int, detections : Array[Detection]) -> FrameTracks raise TrackerErrorfn TrackerConfig::new(high_score_threshold~ : Double, low_score_threshold~ : Double, new_track_threshold~ : Double, first_match_max_cost~ : Double, second_match_max_cost~ : Double, max_lost_frames~ : Int, min_hits~ : Int, fuse_score~ : Bool) -> TrackerConfig raise TrackerErrortype TrackerStatusInstall
Download zipDetector-agnostic tracking and spatial event analytics in MoonBit
Dependencies