news 2026/9/12 22:34:18

Label Studio 图像标注坐标单位详解:LS 百分比(%)与像素(px)的换算原理与实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Label Studio 图像标注坐标单位详解:LS 百分比(%)与像素(px)的换算原理与实战

Label Studio 图像标注坐标单位详解:LS 百分比(%)与像素(px)的换算原理与实战

【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio

Label Studio 的图像类标注(矩形框、多边形、关键点、椭圆等)在注解结果中,xywidthheight等几何字段统一以相对图像宽高的百分比存储,而非像素绝对值。本文以官方文档 docs/source/includes/image_units.md 为核心,完整讲解这一坐标体系的含义、双向换算公式、注解结果(result)的完整结构,并结合前端编辑器源码(web/libs/editor)验证其底层实现,帮助你正确处理导出数据、训练集构建与模型推理结果回写。

读完本文,你将能够:理解 Label Studio 图像标注结果中每个字段的真实含义;在 Python 中自由完成「百分比 ↔ 像素」双向换算;读懂任何图像类标注的 JSON 结果(含旋转与多图场景);并在训练自己的检测模型时准确还原像素级标注框。

为什么图像标注坐标要用百分比而不是像素

在 docs/source/includes/image_units.md 中明确规定:图像标注结果中xywidthheight的单位是占图像整体尺寸的百分比(percentages of overall image dimension)

这并非随意设计,而是由 result_format 所定义的通用注解格式决定的。Label Studio 的每条标注结果(region)在annotation.result下存储为列表项,value字段承载标注动作的几何或语义信息。对于图像类标注,采用归一化百分比坐标有以下实际好处:

  • 与原始图像分辨率解耦:同一份标注结果可以应用于不同分辨率下的同一图像,预览、缩放、适配容器时无需重新换算;
  • 兼容多图与画布缩放:编辑器在画布上渲染时,统一将内部归一化坐标换算为画布坐标(见下文源码分析),导出时则记录原始图像尺寸作为换算基准;
  • 统一各标签类型的存储口径:矩形框、多边形、椭圆、关键点等所有图像类 region 都遵循同一套百分比规则。

核心换算公式:LS 百分比 ↔ 像素

官方文档给出了两组标准换算公式。设original_widthoriginal_height为原始图像的像素宽高,x, y, width, height为标注结果中的百分比数值:

百分比 → 像素(LS → Pixel):

pixel_x = x / 100.0 * original_width pixel_y = y / 100.0 * original_height pixel_width = width / 100.0 * original_width pixel_height = height / 100.0 * original_height

像素 → 百分比(Pixel → LS):

x = pixel_x / original_width * 100.0 y = pixel_y / original_height * 100.0 width = pixel_width / original_width * 100.0 height = pixel_height / original_height * 100.0

需要注意:xy表示区域左上角相对于图像左上角的百分比偏移;widthheight表示区域宽高占图像宽高的百分比。因此理论上它们都应在0~100区间内(编辑器的拖拽范围也受此约束),但通过 API 写入时引擎并不会强制限制,越界值需要自行校验。

完整实战示例:从注解结果到像素框

以下完整代码来自官方文档 image_units.md,包含一个标准的图像矩形框注解 Task 结构,以及两个方向的转换函数与调用验证,可直接复制运行:

task = { "annotations": [{ "result": [ { "...": "...", "original_width": 600, "original_height": 403, "image_rotation": 0, "value": { "x": 5.33, "y": 23.57, "width": 29.16, "height": 31.26, "rotation": 0, "rectanglelabels": [ "Airplane" ] } } ] }] } # convert from LS percent units to pixels def convert_from_ls(result): if 'original_width' not in result or 'original_height' not in result: return None value = result['value'] w, h = result['original_width'], result['original_height'] if all([key in value for key in ['x', 'y', 'width', 'height']]): return w * value['x'] / 100.0, \ h * value['y'] / 100.0, \ w * value['width'] / 100.0, \ h * value['height'] / 100.0 # convert from pixels to LS percent units def convert_to_ls(x, y, width, height, original_width, original_height): return x / original_width * 100.0, y / original_height * 100.0, \ width / original_width * 100.0, height / original_height * 100 # convert from LS output = convert_from_ls(task['annotations'][0]['result'][0]) if output is None: raise Exception('Wrong convert') pixel_x, pixel_y, pixel_width, pixel_height = output print(pixel_x, pixel_y, pixel_width, pixel_height) # convert back to LS x, y, width, height = convert_to_ls(pixel_x, pixel_y, pixel_width, pixel_height, 600, 403) print(x, y, width, height)

针对上述示例数据手工验算:pixel_x = 5.33 / 100 * 600 = 31.98pixel_y = 23.57 / 100 * 403 ≈ 94.99pixel_width = 29.16 / 100 * 600 = 174.96pixel_height = 31.26 / 100 * 403 ≈ 125.98。反向转换后应能恢复出原始的百分比数值(浮点精度导致的微小尾差属正常现象)。

读懂完整的结果字段:original_width / original_height / image_rotation / value

结合前端源码 Image.js 的createSerializedResult实现,可以看到每个图像类 region 的序列化结构由「图像维度元数据 + value」两部分组成:

const imageDimension = { original_width: currentImageEntity.naturalWidth, // 原始图像像素宽 original_height: currentImageEntity.naturalHeight, // 原始图像像素高 image_rotation: currentImageEntity.rotation, // 图像整体旋转角度(度) };

各字段含义如下:

字段类型含义
original_widthnumber原始图像的像素宽度,是百分比换算回像素的基准
original_heightnumber原始图像的像素高度,同理为换算基准
image_rotationnumber图像在画布上的整体旋转角度(度),例如 90/180/270,参与坐标系变换
value.xnumber区域左上角 X(占原图宽度的百分比,0–100)
value.ynumber区域左上角 Y(占原图高度的百分比,0–100)
value.widthnumber区域宽度(占原图宽度的百分比)
value.heightnumber区域高度(占原图高度的百分比)
value.rotationnumber区域自身的旋转角度(度),不随image_rotation改变
value.rectanglelabelsarray该区域命中的标签列表,如["Airplane"]

上述字段的语义注释(含0-100的取值范围说明)在 RectRegion.jsx 的RectRegionResultJSDoc 中也有完整定义,矩形框、椭圆、多边形、关键点等区域类型均继承该坐标体系。

源码级验证:前端如何序列化与换算坐标

为了确证百分比坐标的底层机制,可以从前端编辑器的两个关键实现点验证:

  1. 序列化出口Image.createSerializedResult(Image.js)在每次区域创建/修改时,将当前图像的naturalWidthnaturalHeight与旋转角写入original_widthoriginal_heightimage_rotation,再把内部归一化坐标value一并落盘。相关单元测试(Image.test.js)直接断言了该方法的输出结构,如{ original_width: 100, original_height: 80, image_rotation: 90, value }

  2. 画布坐标换算RectRegion(RectRegion.jsx)通过internalToCanvasX/YcanvasToInternalX/Y在「归一化内部坐标」与「画布像素坐标」之间往返换算,canvasX/canvasY/canvasWidth/canvasHeight均由此得出;而serialize()(同文件 L383-L393)只输出归一化的x, y, width, height, rotation给后端存储。

因此,无论用户在画布上如何缩放、旋转,持久化到数据库的始终是百分比坐标 + 原始图像尺寸,像素计算由读取方按需完成——这正是本文开头公式的工程基础。

使用中的边界情况与注意事项

结合源码与格式定义,实际使用中需留意以下几点:

  • 图像未加载完成时的序列化createSerializedResult中有一条保护逻辑——当图像尚未加载完成且区域携带_rawResult时,会直接克隆原始结果(Image.js),避免以错误的尺寸元数据覆盖已有标注。若你在 API 回写时省略original_width/original_height,编辑器可能无法正确换算显示。
  • 多图场景:使用多图像对象标签时,每个 region 还会带有item_index字段以区分属于哪张子图(Image.js),换算像素时必须使用对应子图的原始尺寸。
  • 旋转区域value.rotation是区域自身旋转角,计算包围盒像素范围时需额外做旋转校正;image_rotation是整图旋转角,二者不要混淆。
  • 浮点精度:反向换算后百分比可能存在极小尾差,比较时建议使用容差而非严格相等。

延伸阅读

  • 注解结果通用结构(region、id、from_name/to_name、perRegion 等):result_format
  • 图像对象标签配置(多图、旋转、缩放等属性):image 标签文档
  • 矩形框标签配置:rectangle 标签文档
  • 前端矩形区域实现与序列化源码:RectRegion.jsx
  • 图像元数据注入与序列化源码:Image.js
  • 测试套件中的真实图像标注样例(含 bbox ground truth):image_urls_with_bboxes_gt.json

掌握「百分比为存储单位、像素为计算单位」这一原则后,无论你是导出标注训练目标检测模型,还是把模型预测结果回写为 Label Studio 注解,都能在两种坐标系间准确无误地往返切换。

【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 22:34:11

YOLOv8 CPU部署实测:PyTorch、ONNX、OpenVINO在i5-14600KF上的性能对比

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 22:34:05

OpenPose轻量部署与养老行为识别实战

简介:本资源是一套基于OpenPose实现的人体姿态检测完整项目,聚焦老年人日常行为监护场景,支持站立、坐姿、躺卧及摔倒等关键状态识别,适用于人工智能、计算机科学等相关专业学生课程设计、毕业设计及企业原型开发。资源包共581个文…

作者头像 李华
网站建设 2026/9/12 22:33:01

随机森林回归实战:从MSE分裂到P10/P90预测区间

简介:随机森林回归的MATLAB实现资源,主要面向需要完成回归预测、变量筛选与特征重要性评估的数据分析人员及机器学习初学者。资源基于集成学习原理,涵盖从数据预处理、模型构建到结果评估的完整流程,可借助TreeBagger或fitrensemb…

作者头像 李华
网站建设 2026/9/12 22:31:57

Lua字符串处理全解析:从基础操作到高级模式匹配

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华