查看 Markdown

使用 Results 对象

每次预测都会为每张图像返回一个 Results 对象。它为每一种载荷准备了一个具名槽位(slot),除了模型实际产出的那些之外全都是空的;导出的产物上也有同样这些槽位。

一个对象,每种载荷一个槽位

对一张图像做一次预测返回一个 Results。它带着二十三个载荷槽位,模型只填充自己 这个任务会产出的那些。其余每个槽位都是 None,所以在检测器上读 result.masks 得到的是 None,而不是报错。

槽位类形状由什么产出
boxesBoxes(N, 4),外加分数和类别检测,以及任何先做定位的任务
masksMasks(N, H, W)实例分割
keypointsKeypoints(N, K, 2) 或 (N, K, 3)姿态
probsProbs(C,)分类
obbOBB(N, 7) 或 (N, 8)旋转框
gazeGaze(N, 2),弧度制的俯仰角和偏航角视线估计
pointsPoints(N, 4),依次是 x、y、类别、置信度点定位
semantic_maskSemanticMask(H, W) 类别 id语义分割
panopticPanopticSegmentation(H, W) 分段 id,外加 segments_info全景分割
depth_mapDepthMap(H, W) 浮点数深度估计
normal_mapNormalMap(H, W, 3) 单位向量表面法线
edgesEdgeMap[0, 1] 区间的 (H, W) 浮点数边缘检测
restoredRestoredImage(H, W, 3) uint8 RGB图像恢复与超分辨率
matteMatte[0, 1] 区间的 (H, W) 浮点数alpha 抠图与背景移除
ocrOCRRegions(N, 4, 2) 多边形,外加转写文本文本检测与识别
embeddingsEmbeddings(N, D),行已做 L2 归一化embed 任务
identitiesIdentitiesN 个名字和分数带图库的 embed 任务
meshesMeshes人体参数,以及可选的顶点人体网格恢复
albedoAlbedoMap(H, W, 3) 线性 RGB反照率估计
boxes3dBoxes3D(N, 14),相机坐标系下的长方体,与 boxes 逐行对齐单目 3D 检测
actionsActions(T, D) 动作块机器人策略
generatedGeneratedImage(H, W, 3) uint8 RGB,加上提示词、种子和设置图像生成
gaussiansGaussiansN 个高斯,包含 xyz、scales、rotations、opacities 和 sh,再加上相机和种子3D 生成

和它们并列的,是每个结果都有的那些字段:orig_shape 是 (height, width), path 是源路径(内存输入时为 None),names 把类别 id 映射到类别名, frame_idx 用于视频和实时帧,track_id 在跟踪时出现,还有 restore_scale, 即恢复类结果的整数放大倍数。

result.normals 是 result.normal_map 的别名。

result.speed 每个结果上都有。集成会用 member_0、 member_1 和 fusion 填充它,单位是毫秒;LibreGen 模型填充 inference,即每生成 一张图像或一个对象所用的毫秒数;FCOS3D 填充 preprocess、inference 和 postprocess。其他模型让它保持为空字典。

Boxes

Boxes
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt")result = model(SAMPLE_IMAGE) print(result.orig_shape)   # 源图像的 (height, width)print(result.path)         # 源路径,内存输入时为 None for xyxy, conf, cls in zip(    result.boxes.xyxy.tolist(),    result.boxes.conf.tolist(),    result.boxes.cls.tolist(),):    print(result.names[int(cls)], round(float(conf), 3), xyxy)
归一化坐标
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt")result = model(SAMPLE_IMAGE) print(result.boxes.xyxy[:1])    # 像素,x1 y1 x2 y2print(result.boxes.xywh[:1])    # 像素,中心 x、中心 y、w、hprint(result.boxes.xyxyn[:1])   # 同一个框除以宽和高print(result.boxes.xywhn[:1])
NumPy 与设备
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt")result = model(SAMPLE_IMAGE) # 下面每一个都返回新的 Results,原对象不变as_numpy = result.numpy()on_cpu = result.cpu() print(type(as_numpy.boxes.xyxy).__name__)print(type(on_cpu.boxes.xyxy).__name__)

Boxes 把坐标和分数分开存成几个数组,而不是打包成一个张量。

属性内容
xyxy(N, 4) 绝对像素,x1 y1 x2 y2
xywh(N, 4) 绝对像素,中心 x、中心 y、宽、高
xyxyn、xywhn同样的值除以图像宽和高
conf(N,) 置信度
cls(N,) 类别 id,浮点数形式
id(N,) 跟踪 id,或者 None
is_trackid 是否已设置
data全部拼接在一起:检测框、可选的 id、conf、cls

cls 是浮点数组,所以要这样用它:result.names[int(cls)]。

xyxyn 和 xywhn 需要 orig_shape,这个字段由 Results 替你填好。

稠密载荷

覆盖整张图像的载荷,行为和逐实例的载荷不同,切片的时候这一点很重要。

SemanticMask 存的是原始画布上的 (H, W) 类别 id,其中 255 被保留为忽略值, 永远不算作一个类别。classes 列出出现过的 id,并把它排除在外;class_mask(id) 返回一个布尔的 (H, W)。

PanopticSegmentation 存的是 (H, W) 分段 id,0 是 void id,另外还有一个 segments_info 字典列表,每项至少带 id 和 category_id。segment_ids 列出 出现过的 id,segment_mask(id) 选出其中一个。

DepthMap 存的是 (H, W) 相对逆深度:值越大表示越近,而且这些值不是以米为 单位的度量值。它提供在有限值上算出的 min、max、mean,以及把范围重新缩放 到 [0, 1] 的 normalized()。

NormalMap 存的是 OpenCV 相机坐标系下的 (H, W, 3) 单位向量,+x 向右, +y 向下,+z 指向场景内部,所以正对相机的表面是 (0, 0, -1)。 assert_normalized() 检查每个像素都是有限值、且长度为 1。

EdgeMap 存的是 [0, 1] 区间的 (H, W) float32。这里保留的是连续图,而不是 阈值化之后的结果,所以选择截断点的地方是 binary(threshold=0.5)。

Matte 存的是 [0, 1] 区间的 (H, W) float32,1 表示完全是前景。array 返回裁剪后的 float32。

RestoredImage 存的是 (H, W, 3) uint8 RGB,array 给出原始 ndarray, save(path) 把它写出去。

Probs 为整张图像存一个概率向量。top1 和 top5 是类别索引,top1conf 和 top5conf 是对应的分数。

Embeddings 存的是已经做过 L2 归一化的 (N, D) 行,所以余弦相似度就是一次 点积。similarity(other) 对图库返回 (N, M),对单个向量返回 (N,); verify(i, j, threshold=0.4) 比较两行。

OCRRegions 按阅读顺序存 (N, 4, 2) 多边形,四个角点依次是左上、右上、右下、 左下。转写文本在 texts 里,识别分数在 conf 里,检测分数在 det_conf 里。 因为这些是真正的旋转多边形,它们不会填充 boxes;需要矩形时,ocr.xyxy 给出 轴对齐的外接框。

切片与搬移

result[i] 返回一个只装着一个实例的新 Results。逐实例的载荷会被切片,整图的 载荷则原样带过去,所以对一个分类结果切片不会把它的概率向量截断成单个类别,对一 个深度结果切片也不会破坏 (H, W) 的布局。

len(result) 数的是实例:检测框、点、嵌入向量、OCR 区域或网格。任何稠密的整图 载荷都算作 1,生成的图像或 3D 对象也算作 1。什么都没装的结果是 0。

to()、cpu()、cuda() 和 numpy() 各自返回一个新的 Results,其中每个已 填充的槽位都转换过。它们不会修改原对象。

update() 是唯一一个原地修改的方法,它替换指定名字的槽位,并返回同一个对象。

JSON

summary 与 to_json
import json from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt")result = model(SAMPLE_IMAGE) rows = result.summary()print(json.dumps(rows[:2], indent=2)) # 同样的内容,只是以字符串返回,关键字参数也一样print(result.to_json(normalize=True, decimals=3)[:200])
CLI
libreyolo predict model=LibreYOLO9s.pt --json \  source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpg

summary() 返回一个由普通字典组成的列表,to_json() 就是把这个列表交给 json.dumps。两者接受同样的三个参数:normalize=False 把坐标切换成 [0, 1], decimals=5 设置四舍五入位数,embeddings=False 控制要不要包含嵌入向量。

每一行的形状跟着载荷走。检测行带 name、class、confidence 和一个 box 字典,有掩码时会加上 segments,旋转框会加上 obb 和 corners,视线会加上以 弧度和角度两种单位给出的 gaze 角度,跟踪时加上 track_id,有网格时加上 mesh 参数。

没有检测框时,由某一个载荷来决定这些行:OCR 每个区域输出一行,带上它的 text,点每个点一行,全景每个分段一行、带 pixel_count 和 pixel_fraction, 语义每个出现过的类别一行,分类输出前五个类别。深度、法线、边缘、恢复和抠图各自 只输出一行汇总,描述的是这张图本身,而不是它的像素;生成的图像或 3D 对象输出一行,带上它的种子和设置。

有两种载荷是有意精简过的。嵌入向量只报告 embedding_dim,因为一行 512 个浮点数 意味着每张脸大约 2 KB;传 embeddings=True 才会把数值也包含进来。网格顶点则 完全不包含,因为那是每个人几万个坐标。要拿几何数据,读 result.meshes.vertices 或者调用 result.meshes.save_obj(path)。

绘制与保存

标注后的图像
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt") # save=True 会画出载荷,并写到 runs/detect/predict* 下result = model(SAMPLE_IMAGE, save=True)print(result.saved_path)

predict(save=True) 才是那条既标注又写盘的路径。它根据哪个槽位被填充来挑选绘制 例程,所以语义结果写出来是一张上色(colorize)后的掩码,深度结果写成深度可视化 图,全景结果带上它的分段,matte 写成透明背景的 RGBA PNG,检测器写成检测框、掩码 画在框下面。写出的路径会作为 result.saved_path 挂在结果上。

Results.save(path) 同样窄:它写出生成的图像或 3D 对象,格式由扩展名决定;把 matte 结果写成透明背景的 RGBA PNG 抠图;其他情况一律抛 NotImplementedError。 Results.cutout() 返回同一个 RGBA 数组,但不写盘。对 matte 来说,两者都需要源图像, 从 result.path 取,或者用 image= 传进来。

有两种载荷自带写盘方法:恢复后的图像用 result.restored.save(path),网格用 result.meshes.save_obj(path, index=0)。

文件最终落在哪里,以及 output_path 和 output_file_format 的行为,见 预测数据源。

plot() 覆盖所有任务载荷。图像叠加默认返回连续的 HxWx3 uint8 BGR;pil=True 请求 PIL。深度、法线、边缘和反照率图,3D 长方体、动作块、生成的图像,以及生成的 3D 对象的点渲染图,默认返回 PIL 图像;pil=False 返回数组。orig_img 为内存和 URL 数据源保留 BGR 像素;本地文件和已收集的有限时长视频帧可以重新打开。

控制项包括 img、conf、labels、boxes、masks、probs、line_width、pil、show、save 和 filename。保存的分类图像包含前五个标签。透明度图保存为 RGBA 抠图。

导出产物返回同样的对象

安装导出用的 extra
pip install "libreyolo[onnx]"
从导出产物拿到同样的 Results
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt")path = model.export(format="onnx")   # 返回写入的路径 # LibreYOLO() 按文件后缀分派exported = LibreYOLO(path)result = exported(SAMPLE_IMAGE) print(type(result).__name__, len(result.boxes))

LibreYOLO() 按文件后缀分派,所以一个导出产物和一个 .pt 检查点(checkpoint) 走同一个调用加载,返回同样的 Results。.onnx、.torchscript、.engine、.pte、 .tflite 和 .mnn 文件按后缀识别,OpenVINO、Paddle、ncnn 和 Core ML .mlpackage 目录以及 Triton 模型 URL 也一样。把一个 模型换成它的导出版本时,读 result.boxes.xyxy 的代码不用改。完整的格式清单见 导出。

反过来,直接去用运行时自己的 API,就意味着预处理、后处理和类别名都得你自己管。

载荷类、槽位、搬移语义、summary()、to_json()、plot()、save() 和 cutout() 读自 libreyolo/utils/results.py。标注和写盘行为读自 libreyolo/models/base/inference.py 里的 InferenceRunner._save_annotated_image 和 libreyolo/utils/general.py 里的 resolve_save_path。后缀分派读自 libreyolo/models/__init__.py 里的 LibreYOLO()。