mbmot

    Detector-agnostic online multi-object tracking in MoonBit

    tracking
    computer-vision
    mot
    Download zip
    Version
    0.1.0
    License
    MIT
    Last updated
    17 hours ago
    Downloads
    2

    Dependencies

    #MBMOT

    MBMOT 是一个用 MoonBit 编写的在线多目标跟踪库。它不读取图片,也不绑定某个检测模型;调用方逐帧交给它检测框、置信度和类别,得到按 track_id 排序的可见轨迹,以及本帧进入 lostremoved 的编号。

    跟踪器采用八维 xyah + velocity 运动状态。高分检测先与活动、暂定和仍在保留期内的 lost 轨迹关联,未匹配的活动轨迹再尝试低分检测。低分框能维持正在活动的身份,但不会新建编号或恢复已经 lost 的轨迹。

    #从源码运行

    需要安装 MoonBit 工具链。检出仓库后可以运行四个稳定后端的检查和测试:

    moon fmt --check moon check --target all --deny-warn moon test --target all --deny-warn

    核心包的基本调用如下:

    let 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)

    BoundingBox::from_xywh 接受左上角加宽高,BoundingBox::from_cxcywh 接受中心点加宽高,后者可直接承接 YOLO 常见的坐标顺序。两种构造都保留输入尺度:像素坐标和归一化坐标可以使用,但同一条流必须保持一致,库不会读取图像尺寸替调用方缩放。

    #逐帧契约

    边界框使用连续坐标 xyxy,面积不采用像素端点的 +1 约定。坐标必须有限且满足 x2 > x1y2 > y1;置信度位于 [0, 1],类别编号非负。不同类别永不关联。

    frame_id 必须严格递增。跳帧按实际帧差推进运动和失踪时间,因此一次跳过若干帧与逐帧提交空检测会到达相同的内部状态。任何非法帧都会整帧拒绝,不消耗编号,也不部分更新已有轨迹。

    公开的 Track::bbox() 始终是最近一次真实检测框;预测框只参与关联。Tracker::status() 可读取活动、暂定和 lost 数量,但不会暴露协方差或预测位置。reset() 会清空流状态,并让下一个身份重新从 1 开始。

    默认参数如下:

    参数作用
    high_score_threshold0.25第一阶段检测下限
    low_score_threshold0.10第二阶段检测下限
    new_track_threshold0.25新建身份的最低分数
    first_match_max_cost0.80高分关联最大代价
    second_match_max_cost0.50低分关联最大代价
    max_lost_frames30lost 身份的保留帧数
    min_hits1轨迹可见前所需命中数
    fuse_scoretrue第一阶段是否融合检测分数

    #NDJSON 重放

    原生重放命令从标准输入逐行读取:

    {"frame":1,"detections":[{"xyxy":[0,0,10,10],"score":0.95,"class_id":0}]}

    在 Bash 中运行仓库内的四帧样例:

    moon run src/replay --target native < examples/replay.ndjson

    PowerShell 可以通过 cmd 使用同一个输入文件:

    cmd /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":[]}

    字段顺序和轨迹顺序固定,仓库中的 examples/replay.expected.ndjson 是完整样例输出。遇到 JSON、检测值或帧号错误时,命令把物理输入行号写到标准错误并以非零状态退出;此前已经写出的完整输出行仍然有效。

    MBMOT 处理单摄像头、轴对齐检测框,不使用外观特征。身份延续由类别、运动预测、IoU、检测分数和生命周期共同决定。

    #License

    TrackerError

    pub(all) suberror TrackerError {
    InvalidBoundingBox(String)
    InvalidScore(Double)
    InvalidClassId(Int)
    InvalidConfig(String)
    NonIncreasingFrame(previous~ : Int, received~ : Int)
    InvalidDetection(index~ : Int)
    } derive(
    Debug
    )

    Errors raised when an MBMOT value or frame violates its input contract.

    TrackerError::message

    fn TrackerError::message(self : TrackerError) -> String

    Returns a stable human-readable description of an input error.

    BoundingBox

    pub struct BoundingBox {
    x1 : Double
    y1 : Double
    x2 : Double
    y2 : Double
    } derive(Eq,
    Debug
    )

    An axis-aligned rectangle in continuous xyxy coordinates.

    BoundingBox::from_cxcywh

    fn BoundingBox::from_cxcywh(center_x : Double, center_y : Double, width : Double, height : Double) -> BoundingBox raise TrackerError

    Creates a box from its center, width, and height.

    This is the coordinate order commonly emitted by YOLO-family detectors.

    BoundingBox::from_xywh

    fn BoundingBox::from_xywh(x : Double, y : Double, width : Double, height : Double) -> BoundingBox raise TrackerError

    Creates a box from its top-left corner, width, and height.

    BoundingBox::new

    fn BoundingBox::new(x1 : Double, y1 : Double, x2 : Double, y2 : Double) -> BoundingBox raise TrackerError

    Creates a valid continuous-coordinate bounding box.

    BoundingBox::to_cxcywh

    fn BoundingBox::to_cxcywh(self : BoundingBox) -> (Double, Double, Double, Double)

    Returns the box as its center, width, and height.

    BoundingBox::to_xywh

    fn BoundingBox::to_xywh(self : BoundingBox) -> (Double, Double, Double, Double)

    Returns the box as its top-left corner, width, and height.

    BoundingBox::x1

    fn BoundingBox::x1(self : BoundingBox) -> Double

    Returns the left coordinate.

    BoundingBox::x2

    fn BoundingBox::x2(self : BoundingBox) -> Double

    Returns the right coordinate.

    BoundingBox::y1

    fn BoundingBox::y1(self : BoundingBox) -> Double

    Returns the top coordinate.

    BoundingBox::y2

    fn BoundingBox::y2(self : BoundingBox) -> Double

    Returns the bottom coordinate.

    Detection

    pub struct Detection {
    bbox : BoundingBox
    score : Double
    class_id : Int
    } derive(Eq,
    Debug
    )

    One detector observation supplied to a tracker update.

    Detection::bbox

    fn Detection::bbox(self : Detection) -> BoundingBox

    Returns the detection box.

    Detection::class_id

    fn Detection::class_id(self : Detection) -> Int

    Returns the non-negative detector class identifier.

    Detection::new

    fn Detection::new(bbox : BoundingBox, score : Double, class_id : Int) -> Detection raise TrackerError

    Creates a detection after validating its score and class identifier.

    Detection::score

    fn Detection::score(self : Detection) -> Double

    Returns the detector confidence in the closed interval [0, 1].

    FrameTracks

    type FrameTracks derive(Eq,
    Debug
    )

    The visible tracks and lifecycle events produced by one update.

    FrameTracks::lost

    fn FrameTracks::lost(self : FrameTracks) -> Array[Int]

    Returns identities that entered the recoverable lost state in this frame.

    FrameTracks::removed

    fn FrameTracks::removed(self : FrameTracks) -> Array[Int]

    Returns identities permanently removed in this frame.

    FrameTracks::tracks

    fn FrameTracks::tracks(self : FrameTracks) -> Array[Track]

    Returns visible tracks sorted by track_id.

    Track

    pub struct Track {
    track_id : Int
    bbox : BoundingBox
    class_id : Int
    score : Double
    first_frame : Int
    last_frame : Int
    hits : Int
    } derive(Eq,
    Debug
    )

    A visible tracked object for one processed frame.

    Track::bbox

    fn Track::bbox(self : Track) -> BoundingBox

    Returns the box observed in the current frame.

    Track::class_id

    fn Track::class_id(self : Track) -> Int

    Returns the class identifier fixed when the track was created.

    Track::first_frame

    fn Track::first_frame(self : Track) -> Int

    Returns the frame in which the identity was created.

    Track::hits

    fn Track::hits(self : Track) -> Int

    Returns the number of detector observations assigned to the identity.

    Track::last_frame

    fn Track::last_frame(self : Track) -> Int

    Returns the most recent frame associated with the identity.

    Track::score

    fn Track::score(self : Track) -> Double

    Returns the most recent detector confidence.

    Track::track_id

    fn Track::track_id(self : Track) -> Int

    Returns the stable identity assigned by this tracker instance.

    Tracker

    type Tracker

    Stateful online association for one ordered frame stream.

    Tracker::new

    fn Tracker::new(config : TrackerConfig) -> Tracker

    Creates an empty tracker with a validated configuration.

    Tracker::reset

    fn Tracker::reset(self : Tracker) -> Unit

    Clears every identity and restarts numbering from 1.

    Tracker::status

    fn Tracker::status(self : Tracker) -> TrackerStatus

    Summarizes retained identity state without exposing motion internals.

    Tracker::update

    fn Tracker::update(self : Tracker, frame_id : Int, detections : Array[Detection]) -> FrameTracks raise TrackerError

    Associates one strictly newer frame and returns its visible identities.

    The entire frame is validated before tracker state changes.

    TrackerConfig

    pub struct TrackerConfig {
    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
    } derive(Eq,
    Debug
    )

    Settings that determine detection selection and identity association.

    TrackerConfig::default

    fn TrackerConfig::default() -> TrackerConfig

    Returns MBMOT's default association settings.

    TrackerConfig::first_match_max_cost

    fn TrackerConfig::first_match_max_cost(self : TrackerConfig) -> Double

    Returns the maximum accepted first-stage assignment cost.

    TrackerConfig::fuse_score

    fn TrackerConfig::fuse_score(self : TrackerConfig) -> Bool

    Reports whether detector scores are fused into first-stage costs.

    TrackerConfig::high_score_threshold

    fn TrackerConfig::high_score_threshold(self : TrackerConfig) -> Double

    Returns the high-confidence cutoff used by the first association stage.

    TrackerConfig::low_score_threshold

    fn TrackerConfig::low_score_threshold(self : TrackerConfig) -> Double

    Returns the low-confidence cutoff reserved for the second association stage.

    TrackerConfig::max_lost_frames

    fn TrackerConfig::max_lost_frames(self : TrackerConfig) -> Int

    Returns the configured number of frames retained after a track is lost.

    TrackerConfig::min_hits

    fn TrackerConfig::min_hits(self : TrackerConfig) -> Int

    Returns the number of hits required before a track becomes visible.

    TrackerConfig::new

    fn 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 TrackerError

    Creates a complete tracker configuration.

    TrackerConfig::new_track_threshold

    fn TrackerConfig::new_track_threshold(self : TrackerConfig) -> Double

    Returns the minimum score allowed to create a new track.

    TrackerConfig::second_match_max_cost

    fn TrackerConfig::second_match_max_cost(self : TrackerConfig) -> Double

    Returns the maximum second-stage cost reserved for low-score recovery.

    TrackerStatus

    type TrackerStatus

    A read-only summary of identities retained by a tracker.

    TrackerStatus::active_count

    fn TrackerStatus::active_count(self : TrackerStatus) -> Int

    Returns the number of confirmed identities visible in the latest frame.

    TrackerStatus::last_frame

    fn TrackerStatus::last_frame(self : TrackerStatus) -> Int?

    Returns the most recently accepted frame, or None before the first update.

    TrackerStatus::lost_count

    fn TrackerStatus::lost_count(self : TrackerStatus) -> Int

    Returns the number of confirmed identities retained for recovery.

    TrackerStatus::tentative_count

    fn TrackerStatus::tentative_count(self : TrackerStatus) -> Int

    Returns the number of active identities still below min_hits.