查看 Markdown

点检测

点检测为每个物体返回一个 x, y 位置,而不是一个检测框。LibreYOLO 把它做成 point 任务,一次预测里每个物体占一行,包含 x、y、类别和置信度。

定义

point 任务用一个 x, y 坐标加一个类别来定位每个物体,没有宽、没有高,也没有掩码。 因为一次预测就是一个扁平的物体列表,行数就是物体数量,这正是它成为计数任务的原因。

一次预测会填充 result.points,它是一个 Points 载荷,包裹着一个 (N, 4) 数组, 每行是原图像素坐标下的 x, y, class, confidence.xy 返回坐标,.xyn 返回同样的 坐标除以图像尺寸后的结果,.cls 返回类别索引,.conf 返回分数;len() 返回点的 数量。result.boxes 保持为空,所以 ioumax_det 没有作用对象。

模型

有三个家族支持 point,而且它们之间不能互相替代。

FOMO 是固定词汇表的那个选项:一个网格分类器,把低分辨率网格的 每个格子判为背景或物体中心。它是 LibreYOLO 唯一能训练的点家族,也是唯一能导出的。

LocateAnything 接受文本而不是类别索引,所以词汇表就 是你写下的任意短语。它需要 vlm 这个额外依赖,构造方式是 LibreLocateAnything 而 不是走 LibreYOLO() 工厂函数,而且它的权重限定为非商业用途。确切条款,以及这个 检查点(checkpoint)叠加的另外两个许可证,都在它自己的页面上。

SenseNova-Vision 通过它服务另外六个任务时用的同一个 提示式生成检查点来支持 point,加载方式是 LibreVLM("sensenova-vision", task="point")。它需要 sensenova 这个额外依赖,而且 每次预测都是在一个 7B 模型上跑一遍生成,所以单图延迟会明显高于专门设计的检测器。它的 权重是非商业的;许可证在它自己的页面上。

预测

LibreFOMO 权重是本站唯一不自动下载的例外。LibreYOLO("LibreFOMOs-point.pt") 会在 磁盘上找这个文件,找不到就抛出一个指名它的 ValueError,而不是去下载它。先从 Hugging Face 上的 LibreYOLO 组织下载一个检查点, 然后按本地路径加载,或者自己训练一个。

预测点并计数
from libreyolo import LibreYOLO, SAMPLE_IMAGE # LibreFOMO 权重不会自动下载,先从# https://huggingface.co/LibreYOLO 拿一个检查点,再按本地路径加载model = LibreYOLO("./LibreFOMOs-point.pt")result = model(SAMPLE_IMAGE, save=True) points = result.pointsprint(len(points))     # 物体数量print(points.xy)       # (N, 2) 中心点,单位是原图像素print(points.cls, points.conf)
归一化坐标与分类别计数
from collections import Counter from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("./LibreFOMOs-point.pt")result = model(SAMPLE_IMAGE) points = result.points.numpy()print(points.xyn)                          # 同样的中心点,取值在 [0, 1]print(Counter(points.cls.astype(int).tolist()))

文件名必须带上 -point 这个任务后缀,加载器才能识别它。predict(..., nms_radius=1) 控制两个 FOMO 检测点在网格上至少要相隔几个格子才能同时保留下来。输入源、流式处理和 结果处理见预测

数据集格式

point 没有自己的标注格式。点家族读取标准的 YOLO 检测布局,从每一行检测框里推导出 一个中心点,所以 cx cy 就是那个点,而 w h 只决定这一行是否有效。

dataset/
  data.yaml
  images/
    train/scene.jpg
    val/scene.jpg
  labels/
    train/scene.txt
    val/scene.txt

每个标注文件里每个物体占一行,坐标是归一化的:

<class_id> <cx> <cy> <w> <h>
yaml
path: dataset
train: images/train
val: images/val
nc: 1
names: {0: seedling}

标注文件缺失或为空表示没有物体。完整的约定见 数据集格式

训练

FOMO 是唯一实现了训练的点家族。LocateAnything 和 SenseNova-Vision 上的 train() 会 抛出 NotImplementedError;这两个请在上游微调,再把结果加载进来。

在 YOLO 数据集上训练 FOMO
from libreyolo import LibreYOLO model = LibreYOLO("./LibreFOMOs-point.pt")model.train(data="my-dataset.yaml", epochs=40, batch=32, lr0=3e-4)
用训练好的检查点预测
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("./LibreFOMOs-point.pt")results = model.train(data="my-dataset.yaml", epochs=40) # train() 会把最优检查点重新加载回同一个对象,所以调用一返回,# 模型用的就是训练好的权重print(results["best_checkpoint"])print(model(SAMPLE_IMAGE).points.xy)

imgsz 对 FOMO 来说不能随便选:它默认取所加载检查点的原生分辨率,传入别的值会抛出 一个指明期望尺寸的 ValueError。数据集、日志记录器和多卡训练见训练, 这个家族的默认值见 FOMO 页面

验证

val() 用匈牙利算法把预测点和真值(ground truth)点一对一匹配,并在一组距离阈值上 做扫描。一个阈值就是归一化图像坐标下的欧氏距离,默认扫描是从 0.01 到 0.10 的十个值。

验证并读取指标键
from libreyolo import LibreYOLO model = LibreYOLO("./LibreFOMOs-point.pt")metrics = model.val(data="my-dataset.yaml") print(metrics["metrics/precision"], metrics["metrics/recall"])print(metrics["metrics/f1"])print(metrics["metrics/mAP@[0.01:0.10]"])   # fitnessprint(metrics["metrics/MLE"])               # 平均定位误差print(metrics["metrics/MAE"], metrics["metrics/RMSE"])   # 计数误差
修改距离阈值
from libreyolo import LibreYOLO model = LibreYOLO("./LibreFOMOs-point.pt") # 扫描的上下界是键名文本的一部分,所以自定义扫描会给它产生的# mAP 键改名metrics = model.val(data="my-dataset.yaml", dist_thresholds=[0.02, 0.05]) print(metrics["metrics/mAP@0.02"])print(metrics["metrics/mAP@[0.02:0.05]"])

metrics/precisionmetrics/recallmetrics/f1 是在扫描里最严格的那个阈值下按 类别做宏平均得到的,默认是 0.01。metrics/mAP@0.01 是同一个阈值下的平均精度, metrics/mAP@[0.01:0.10] 是整个扫描上的均值。这个扫描值同时也是 fitness,即最优 检查点选择读取的那个数字。两个 mAP 键都是用当前所用的阈值拼出来的,所以传入 dist_thresholds= 会给它们改名。

metrics/MLE 是最严格阈值下匹配点对之间的平均距离,单位同样是归一化的。 metrics/MAEmetrics/RMSE 是计数指标而不是定位指标:它们衡量的是每张图上预测 点数量和真值点数量之间的差。

FOMO 在这些之上还加了第二组、网格级别的指标。它会扫描置信度和 nms_radius,把 F1 最好的那组组合发布为 metrics/grid_F1metrics/grid_precisionmetrics/grid_recallmetrics/grid_mean_distancemetrics/grid_TPmetrics/grid_FPmetrics/grid_FN,产生这组结果的设置则放在 decode/thresholddecode/nms_radius 下面。

导出

FOMO 走共用的导出路径,导出产物按文件后缀通过 LibreYOLO() 加载回来,所以一个 .onnx.engine 文件的表现和检查点一样,返回同样的 Results

导出
from libreyolo import LibreYOLO model = LibreYOLO("./LibreFOMOs-point.pt")model.export(format="onnx")
运行导出的文件
from libreyolo import LibreYOLO, SAMPLE_IMAGE # 工厂函数按文件后缀分发,所以导出产物的加载方式和检查点一样,# 返回的也是同一个 Results 对象model = LibreYOLO("./LibreFOMOs-point.onnx")result = model(SAMPLE_IMAGE) print(result.points.xy)

分格式的支持情况在 FOMO 页面完整导出矩阵上。 LocateAnything 和 SenseNova-Vision 不支持导出:export() 在两者上都会抛异常,因为 生成式模型没有可追踪的检测图。

已针对 LibreYOLO v1.5.0 验证。