EdgeCrafter
一个紧凑的视觉 transformer,面向边缘硬件上的稠密预测,上游以三个同胞模型的形式发布:ECDet、ECPose 和 ECSeg。LibreYOLO 把三者作为一个家族加载,任务由检查点(checkpoint)携带。
- 任务
- detection, pose, instance segmentation
- 尺寸
- s, m, l, x at 640 px
- 安装
pip install libreyolo- 支持层级
- 核心,自 v1.2.0 起。核心的可训练检测器:功能在同一个发布波次里紧跟旗舰。
- 上游
- EdgeCrafter,由 Intellindust AI Lab 发布,采用 Apache-2.0 original COCO weights; restricted obj2coco weights 许可。论文、源码
- 许可
- 代码采用 Apache-2.0,权重采用 Apache-2.0 original COCO weights; restricted obj2coco weights。商用
安装
EdgeCrafter 不需要任何可选 extra。它导入的一切都在基础安装里。
pip install libreyolo用 lora=True 做适配器微调是个例外,它需要 lora extra。
pip install "libreyolo[lora]"预测
权重在首次使用时从 Hugging Face 下载,并缓存在本地。
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreECs.pt")result = model(SAMPLE_IMAGE, save=True) for box in result.boxes: print(box.cls, box.conf, box.xyxy)libreyolo predict model=LibreECs.pt source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpg save=Truefrom libreyolo import LibreYOLO, SAMPLE_IMAGE # 文件名里的 -pose 后缀会选中关键点 head,所以这里不需要传# task 参数model = LibreYOLO("LibreECs-pose.pt")result = model(SAMPLE_IMAGE, save=True) print(result.keypoints.xy)print(result.boxes.conf)from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreECs-seg.pt")result = model(SAMPLE_IMAGE, save=True) print(result.masks.data.shape)任务来自文件名,所以 -pose 或 -seg 检查点会选中自己的 head,不需要传 task
参数。三者返回的都是每个家族都会返回的那个 Results 对象,姿态额外多出
result.keypoints,分割额外多出 result.masks。姿态只覆盖一个类别,也就是人,
带 17 个 COCO 关键点,数量在模型构建时就固定了。它没有检测框 head,所以每个姿态
框都是自身关键点的包围范围,而第三个关键点通道是一个常数,不是逐点的分数。
conf 和 max_det 过滤 query 的选择;iou 为了 API 一致性会被接受,但没有任何
作用,因为三个 head 解码的都是一组 query,没有 NMS 步骤。数据源、流式处理和结果
处理见预测。
检测预测接受矩形 imgsz=(height, width);训练和导出各自保留原有的形状约束。
变体
四种尺寸。它们跑的输入分辨率都相同,所以表格按参数量和精度来区分。
| 检查点 | 输入(px) | mAP 50-95 | 参数量(M) |
|---|---|---|---|
| LibreECl | 640 | 60.1 | 32.97 |
| LibreECm | 640 | 58.4 | 19.43 |
| LibreECs | 640 | 54.3 | 9.88 |
| LibreECx | 640 | 61.1 | 49.94 |
COCO val2017, 500 images。数据由 LibreYOLO 基准测试工具测得,并发布在 Vision Analysis 上,可在此比较不同硬件和运行时的延迟,并查看完整的运行记录。
上游把 ECDet、ECPose 和 ECSeg 发布成三个独立的模型,而不是一个带三个 head 的 模型。它们共用 ECViT 骨干和混合编码器,只在 head 上有差别,所以 LibreYOLO 把它们 收进同一个家族,让检查点文件名来携带任务。因此同一个尺寸字母在三者之间意味着相同 的骨干和编码器,而且不管你加载的是哪一个,预测、验证和导出接受的参数都一样。
训练
三个任务都通过 train() 训练,它从加载的检查点里读出任务,再挑选对应的训练器。
from libreyolo import LibreYOLO model = LibreYOLO("LibreECs.pt")model.train( data="my-dataset.yaml", epochs=50, imgsz=640, batch=8, lr0=5e-4,)libreyolo train model=LibreECs.pt data=my-dataset.yaml epochs=50 imgsz=640 batch=8 lr0=5e-4from libreyolo import LibreYOLO # 需要一个单类别的关键点数据集,它的 data.yaml 里要声明# kpt_shape,并且 imgsz 取检查点的原生尺寸model = LibreYOLO("LibreECs-pose.pt")model.train( data="my-pose-dataset.yaml", epochs=50, imgsz=640,)from libreyolo import LibreYOLO # 需要多边形标注,并且 imgsz 取检查点的原生尺寸model = LibreYOLO("LibreECs-seg.pt")model.train( data="my-dataset.yaml", epochs=50, imgsz=640,)from libreyolo import LibreYOLO model = LibreYOLO("LibreECs.pt")model.train( data="my-dataset.yaml", epochs=50, lora=True,)检测和分割上已经验证过的是:与上游的推理一致性在 1e-5 以内,逐层、逐尺寸都比对过,
以及损失函数和单步训练能在合成输入上跑通。按 train() 自己的 docstring,尚未验证
的是:完整微调的收敛性、多卡训练、停止数据增强后重新加载最佳权重这一步,以及
Objects365 到 COCO 的类别重映射。姿态这条路走的是 DETRPose 已发布的配方,即在类别、
关键点 L1 和 OKS 代价上做匈牙利匹配,并配合对比式关键点去噪,它的收敛性同样没有
端到端验证过。
保持默认时,训练器以 lr0=5e-4 跑 74 轮,混合精度开启,遵循上游的配方:AdamW、
平坦的余弦调度、0.9999 的 EMA 以及 ImageNet 归一化的输入。姿态和分割都要求 imgsz
取检查点的原生尺寸,因为它们的评估锚点网格是在模型构建时建好的;换成别的值会在
运行开始之前就抛错。姿态还要求数据集是单类别的,它的 data.yaml 里要声明
kpt_shape,关键点数量要和 head 匹配。
lora=True 只适用于检测;姿态和分割碰到它会抛出 ValueError。在 Apple silicon
上,训练器把整个训练留在 GPU 上,只把一个操作发到 CPU:可变形注意力内部的
grid-sample 反向传播,PyTorch 没有在 Metal 里实现它。
数据集、数据增强、多卡训练和日志记录器见训练。
验证
val() 返回一个以指标名为键的字典,并在 verbose 保持开启时打印每个类别的结果。
from libreyolo import LibreYOLO model = LibreYOLO("LibreECs.pt")metrics = model.val(data="my-dataset.yaml") print(metrics["metrics/mAP50-95"])print(metrics["metrics/mAP50"])libreyolo val model=LibreECs.pt data=my-dataset.yamlfrom libreyolo import LibreYOLO model = LibreYOLO("LibreECs-pose.pt")metrics = model.val(data="my-pose-dataset.yaml") print(metrics["metrics/keypoints_mAP50-95"])print(metrics["metrics/keypoints_mAP50"])from libreyolo import LibreYOLO model = LibreYOLO("LibreECs-seg.pt")metrics = model.val(data="my-dataset.yaml") print(metrics["metrics/mAP50-95(M)"]) # 掩码print(metrics["metrics/mAP50-95(B)"]) # 检测框姿态在 metrics/keypoints_* 下报告关键点的 OKS 指标。分割把掩码放在普通的
metrics/mAP50-95 键里,并在同一次运行中给出两种视角,检测框在 (B) 下,掩码在
(M) 下。
导出
| 任务 | ONNX | TorchScript | ExecuTorch | TensorRT | OpenVINO | Paddle | MNN | RKNN | ncnn | TFLite | CoreML | Core AI |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Detection | Detection to ONNX:支持 | Detection to TorchScript:支持 | Detection to ExecuTorch:支持 | Detection to TensorRT:支持 | Detection to OpenVINO:支持 | Detection to Paddle:支持 | Detection to MNN:支持 | Detection to RKNN:不支持 | Detection to ncnn:不支持 | Detection to TFLite:不支持 | Detection to CoreML:不支持 | Detection to Core AI:支持 |
| Pose | Pose to ONNX:支持 | Pose to TorchScript:支持 | Pose to ExecuTorch:支持 | Pose to TensorRT:支持 | Pose to OpenVINO:支持 | Pose to Paddle:支持 | Pose to MNN:不支持 | Pose to RKNN:不支持 | Pose to ncnn:不支持 | Pose to TFLite:不支持 | Pose to CoreML:不支持 | Pose to Core AI:不支持 |
| Instance segmentation | Instance segmentation to ONNX:支持 | Instance segmentation to TorchScript:支持 | Instance segmentation to ExecuTorch:支持 | Instance segmentation to TensorRT:支持 | Instance segmentation to OpenVINO:支持 | Instance segmentation to Paddle:支持 | Instance segmentation to MNN:不支持 | Instance segmentation to RKNN:不支持 | Instance segmentation to ncnn:不支持 | Instance segmentation to TFLite:不支持 | Instance segmentation to CoreML:不支持 | Instance segmentation to Core AI:不支持 |
导出的产物按文件后缀经由 LibreYOLO() 重新加载,所以一个 .onnx 或 .engine
文件的表现和检查点一样,返回同样的 Results。姿态和分割导出时用的是固定的
640×640 输入,而不是动态形状,几个检测目标同样是固定画布,包括 OpenVINO、Paddle、
MNN、ExecuTorch 和 Core AI。导出列出了每种格式接受的参数,以及
其中少数几种额外增加的参数。
from libreyolo import LibreYOLO model = LibreYOLO("LibreECs.pt")model.export(format="onnx", imgsz=640)model.export(format="tensorrt", imgsz=640, half=True)libreyolo export model=LibreECs.pt format=onnx imgsz=640libreyolo export model=LibreECs-pose.pt format=onnx imgsz=640libreyolo export model=LibreECs-seg.pt format=onnx imgsz=640from libreyolo import LibreYOLO, SAMPLE_IMAGE # 工厂按文件后缀分发,所以导出的产物加载方式和任何检查点一样,# 返回的也是同一个 Results 对象model = LibreYOLO("LibreECs.onnx")result = model(SAMPLE_IMAGE) print(result.boxes.xyxy)ONNX 输入与输出
| 张量 | images float32 [batch, 3, height, width] |
|---|---|
| 颜色 | RGB |
| 缩放 | 像素 / 255 |
| 归一化 | 均值 [0.485,0.456,0.406], 标准差 [0.229,0.224,0.225] |
| 缩放方式 | 拉伸到 宽 x 高(不填充,不保持宽高比), Pillow bilinear |
如何复现 Pillow 的双线性缩放
LibreYOLO 使用 Pillow 的 BILINEAR 滤波器(Image.resize)缩放。它不是 OpenCV 的 INTER_LINEAR 或 INTER_AREA:缩小时,Pillow 会按缩小倍数加宽滤波器,而 OpenCV 的缩放会改变检测结果,并使框移动数个像素。要在不使用 Pillow 的情况下复现它,先缩放一个轴,舍入到 8 位,再缩放另一个轴并再次舍入。对每个轴,令 s = in_size / out_size,f = max(s, 1)。输出像素 i 的中心为 c = (i + 0.5) * s,读取源像素 j,范围从 int(c - f + 0.5) 到 int(c + f + 0.5) - 1(限制在图像内),每个像素的权重为 max(0, 1 - |j + 0.5 - c| / f),再除以权重之和。Pillow 使用定点运算,并根据图像形状选择轴的顺序,因此浮点权重在少数像素上可能与它相差一个等级。这不会改变我们检查中的结果。
Square input only. Export rejects a non-square imgsz, and height and width are 640 by default.
Detect and segment: batch size 1 only. The graph declares a dynamic batch axis, but batches of 2 or more fail in 1.7.0. Pose runs with any batch size.
For a bit-exact match, use Pillow's integer math: multiply each weight by 2^22 and round half away from zero, sum weight x pixel in integers, add 2^21, shift right by 22 and clip to 0 to 255.
Divide by 255 after the resize, then subtract the ImageNet mean and divide by the ImageNet std per channel.
Detection,默认导出
| 输出 | 形状 | 含义 |
|---|---|---|
pred_logits | [batch, queries, nc] | Raw class logits per query, sigmoid not applied. 300 queries and 80 classes for the COCO checkpoints. |
pred_boxes | [batch, queries, 4] | Boxes as cx, cy, w, h, normalized 0 to 1 of the input. |
后处理
- Apply sigmoid to pred_logits.
- Flatten the scores to queries x nc and take the top max_det (300 by default) over all queries x classes. For flat index k, the query is k // nc and the class is k % nc. One query can yield several classes.
- Keep the entries whose score is strictly above the confidence threshold (0.25 by default). There is no NMS.
- Convert the query's box to x1, y1, x2, y2 with x1 = cx - w / 2, y1 = cy - h / 2, x2 = cx + w / 2, y2 = cy + h / 2.
- Map boxes back to the original image: multiply x by the original width and y by the original height. The input was stretched, so no pad offset applies. LibreYOLO does not clip these boxes to the image.
- nms=True is not available for this family: export raises NotImplementedError.
Instance segmentation,默认导出
| 输出 | 形状 | 含义 |
|---|---|---|
pred_logits | [batch, queries, nc] | Raw class logits per query, sigmoid not applied. 300 queries and 80 classes for the COCO checkpoints. |
pred_boxes | [batch, queries, 4] | Boxes as cx, cy, w, h, normalized 0 to 1 of the input. |
pred_masks | [batch, queries, mask_h, mask_w] | Raw mask logits per query over the whole input canvas, sigmoid not applied. mask_h and mask_w are a quarter of the input, 160 x 160 at 640 x 640. |
后处理
- Select detections exactly as for detect: sigmoid on pred_logits, top max_det (300 by default) over all queries x classes, keep scores strictly above the confidence threshold (0.25 by default). There is no NMS.
- Convert and map the boxes as for detect: cx, cy, w, h to x1, y1, x2, y2, then multiply x by the original width and y by the original height. LibreYOLO does not clip these boxes to the image.
- For each kept detection, take the mask logits of its query and resize them straight to the original image size with bilinear interpolation, half-pixel centers (align_corners=False) and no antialiasing. cv2.resize with INTER_LINEAR on float32 does this.
- The mask is the pixels whose resized logit is above 0. Masks are not cropped to the box.
- nms=True is not available for this family: export raises NotImplementedError.
Pose,默认导出
| 输出 | 形状 | 含义 |
|---|---|---|
pred_logits | [batch, queries, 2] | Raw logits per query, sigmoid not applied. 60 queries. Index 1, the last one, is the person class. Index 0 is an unused slot. |
pred_keypoints | [batch, queries, num_keypoints * 2] | Keypoints as x0, y0, x1, y1 and so on, normalized 0 to 1 of the input. num_keypoints is 17 (COCO order) and is recorded in the num_keypoints metadata key. There is no box output and no keypoint visibility or confidence. |
后处理
- Apply sigmoid to the last logit of each query. That is the person score.
- Sort the queries by score, highest first, and keep at most max_det (300 by default, so all 60 queries). Keep the queries whose score is at or above the confidence threshold (0.25 by default). Note at or above here, strictly above for detect and segment. There is no NMS.
- Reshape each kept query's keypoints to [num_keypoints, 2]. Map them back to the original image: multiply x by the original width and y by the original height.
- The box is the extent of the mapped keypoints: x1, y1 are the minimum x and y, x2, y2 the maximum. The class is always 0 (person).
- LibreYOLO reports visibility 1 for every keypoint, since the graph predicts xy only.
- nms=True is not available for this family: export raises NotImplementedError.
要检查你自己的实现,请用 LibreYOLO 运行同一个文件:LibreYOLO("model.onnx").predict("image.jpg") 会返回参考结果。 已用 LibreYOLO 1.7.0 验证。
检查点
这个家族已发布的全部权重文件。
| 文件 | 输入(px) | 权重许可 |
|---|---|---|
| Detection | ||
| LibreECs.pt | 640 | apache-2.0 |
| LibreECm.pt | 640 | apache-2.0 |
| LibreECl.pt | 640 | apache-2.0 |
| LibreECx.pt | 640 | apache-2.0 |
| LibreECl-obj2coco.pt | other | |
| LibreECm-obj2coco.pt | other | |
| LibreECs-obj2coco.pt | other | |
| LibreECx-obj2coco.pt | other | |
| Pose | ||
| LibreECs-pose.pt | 640 | apache-2.0 |
| LibreECm-pose.pt | 640 | apache-2.0 |
| LibreECl-pose.pt | 640 | apache-2.0 |
| LibreECx-pose.pt | 640 | apache-2.0 |
| LibreECl-pose-obj2coco.pt | other | |
| LibreECm-pose-obj2coco.pt | other | |
| LibreECs-pose-obj2coco.pt | other | |
| LibreECx-pose-obj2coco.pt | other | |
| Instance segmentation | ||
| LibreECs-seg.pt | 640 | apache-2.0 |
| LibreECm-seg.pt | 640 | apache-2.0 |
| LibreECl-seg.pt | 640 | apache-2.0 |
| LibreECx-seg.pt | 640 | apache-2.0 |
| LibreECm-seg-obj2coco.pt | other | |
| LibreECs-seg-obj2coco.pt | other | |
| LibreECl-seg-obj2coco.pt | other | |
| LibreECx-seg-obj2coco.pt | other | |
上面的每个文件目前都在 LibreYOLO 组织中,并会在首次使用时下载。
obj2coco 变体覆盖检测、分割和姿态。下载需要主动选择,其上游条款限制商用。原始 COCO 检查点保留记录的 Apache-2.0 授权。
许可证
请检查你所下载的具体权重在 Hugging Face 仓库中的许可。LibreYOLO 组织里的每个检查点都附有许可,同一家族内也不一定相同。该仓库是权威来源;以下摘要说明本页上次验证时适用的情况。
这里只说明涉及的许可证,不构成法律意见。如果答案对商用很重要,请自行阅读许可证并咨询法律顾问。
- 原始工作
- EdgeCrafter, Intellindust AI Lab
- 上游许可
- Apache-2.0 original COCO weights; restricted obj2coco weights
- LibreYOLO 代码
- MIT
- 权重
- 采用 Apache-2.0 original COCO weights; restricted obj2coco weights 许可,重新发布在 huggingface.co/LibreYOLO
- 解读
- Original COCO checkpoints retain their recorded Apache-2.0 grant. The obj2coco detection, segmentation and pose variants require explicit opt-in and retain non-commercial upstream terms. The library code license does not replace those weight terms.
引用
@article{liu2026edgecrafter,
title={EdgeCrafter: Compact ViTs for Edge Dense Prediction via Task-Specialized Distillation},
author={Liu, Longfei and Hou, Yongjie and Li, Yang and Wang, Qirui and Sha, Youyang and Yu, Yongjun and Wang, Yinzhi and Ru, Peizhe and Yu, Xuanlong and Shen, Xi},
journal={arXiv},
year={2026}
}复制自作者在 github.com/Intellindust-AI-Lab/EdgeCrafter#-citation 上提供的引用块。