Skip to main content
Version: Next

标注工具

简介

使用人工智能解决现实场景中的问题,数据标注是不可或缺的一环,准确的标注数据都是训练高性能AI模型的基础。MegaPrecVision加一个盛相Hdc相机可以轻松搭建标注工具,可以快速实现轻量级的在线采图和缺陷标注。

演示的标记目标是一批金属零件,表面有脏污、划痕、磕碰等缺陷,通过本案例工具可以实现图像采集和保存、缺陷标注和文件保存,最终用于后续的AI训练。

全程讲解视频

标注工具

environmentTable

上手搭建流程

1.通过 TCP 通讯 接收上位机发送的工件 SN 号,触发拍摄
2.图像采集和将图像保存在本地
3.加入区域模块用于图像显式标注,通过lua脚本实现标注信息文件保存
4.将流程关联到运行界面

environmentTable

详细流程

序号模块名称主要作用
1起始流程入口
2接收数据解析 TCP 数据成变量(含 SN)
3混合相机按当前配置触发拍照,输出图像
4矩形区域可视化标注区域
5时间获取获取当前日期时间字符串
7字符串运算(Beta)对 SN / 日期进行第一次字符串处理
8格式化字符串将时间字符串进一步格式化,生成文件名前缀
9数据保存使用前缀保存 PNG 图像
10条件分支根据条件执行不同缺陷脚本
111-划伤保存调用 Lua 脚本获取并保存“划伤”标注(JSON)
122-磕碰保存调用 Lua 脚本获取并保存“磕碰”标注(JSON)
13汇合将分支重新汇合,回到主流程
14结束流程结束

Lua示例脚本

-- 来自脚本模块传入的全局变量:
-- x1, y1, x2, y2 : 实数(矩形框两个角)
-- label : 文本(字符串),例如 "impact_crack" / "internal_crack"
-- time, sn : 文本(字符串,用于文件名)
-- imageHeight : 整数(图像高度)
-- imageWidth : 整数(图像宽度)
--
-- C++ 中已经注册了 json 模块:
-- json.encode / json.decode / json.save / json.load / json.null

------------------------------------------------------------
-- 1. 工具函数:把 sn / time 里的非法字符替换掉,避免 Win 下文件名报错
------------------------------------------------------------
local function sanitize_for_filename(s)
s = tostring(s or "")
-- 只保留字母数字、下划线、短横线,其余全部换成下划线
return (s:gsub("[^%w%-_]", "_"))
end

------------------------------------------------------------
-- 2. 生成文件完整路径:label json + image 路径
------------------------------------------------------------
local sn_str = sanitize_for_filename(sn)
local time_str = sanitize_for_filename(time)

-- 标注 json 保存目录(需要修改)
local label_dir = "C:\\Users\\****\\Desktop\\label\\"
-- 如需自动创建目录,可放开下行(需要系统支持 mkdir)
-- os.execute('mkdir "' .. label_dir .. '" 2>nul')

-- 图像目录(需要修改)
local img_dir = "C:\\Users\\****\\Desktop\\image\\"

-- 基础文件名:sn_time
local base_name = string.format("%s_%s", sn_str, time_str)

-- json 文件名和完整路径
local json_filename = base_name .. ".json"
local json_filepath = label_dir .. json_filename

-- imagePath 按 img_dir/sn_time.png 的形式
local image_filename = base_name .. ".png"
local imagePath = img_dir .. image_filename

------------------------------------------------------------
-- 3. 构造当前标注的 shape 结构
-- 目标格式:
-- {
-- "label": "impact_crack",
-- "points": [ [x1,y1], [x2,y2] ],
-- "group_id": null,
-- "description": "",
-- "shape_type": "rectangle",
-- "flags": {},
-- "mask": null
-- }
------------------------------------------------------------
local x1_num = tonumber(x1) or 0.0
local y1_num = tonumber(y1) or 0.0
local x2_num = tonumber(x2) or 0.0
local y2_num = tonumber(y2) or 0.0
local label_str = tostring(label or "")

local current_shape = {
label = label_str,
points = {
{ x1_num, y1_num },
{ x2_num, y2_num },
},
group_id = json.null, -- 严格输出为 null
description = "",
shape_type = "rectangle",
flags = {},
mask = json.null, -- 严格输出为 null
}

------------------------------------------------------------
-- 4. 从已存在的 json 文件中加载数据(若不存在则创建新的结构)
------------------------------------------------------------
local data, load_err = json.load(json_filepath)
local shapes = nil

if not data then
-- 文件不存在或解析失败:从零开始构建一个结构
data = {}
shapes = {}
data.shapes = shapes
else
-- 文件存在:保证有 shapes 数组
data.shapes = data.shapes or {}
shapes = data.shapes
end

-- imageHeight / imageWidth 从 C++ 传入(整数)
local h = tonumber(imageHeight) or 0
local w = tonumber(imageWidth) or 0

-- 每次都更新图像信息,保持与当前 SN/time 对应
data.imagePath = imagePath
data.imageHeight = h
data.imageWidth = w

------------------------------------------------------------
-- 5. 检查当前 shape 是否已经存在,避免重复写入
------------------------------------------------------------
local function same_point(p1, p2)
return p1[1] == p2[1] and p1[2] == p2[2]
end

local function same_shape(a, b)
if not a or not b then
return false
end
if a.label ~= b.label then
return false
end
if a.shape_type ~= b.shape_type then
return false
end
if not a.points or not b.points then
return false
end
if #a.points ~= 2 or #b.points ~= 2 then
return false
end
-- 只比较 label + shape_type + 两个角点,group_id/mask 不参与去重
return same_point(a.points[1], b.points[1])
and same_point(a.points[2], b.points[2])
end

local already_has_shape = false
for _, s in ipairs(shapes) do
if same_shape(s, current_shape) then
already_has_shape = true
break
end
end

------------------------------------------------------------
-- 6. 若不存在相同 shape,则追加到 shapes 中
------------------------------------------------------------
if not already_has_shape then
table.insert(shapes, current_shape)
end

------------------------------------------------------------
-- 7. 保存回 json 文件
-- json.save(value, path) -> true 或 false, err
------------------------------------------------------------
local ok, save_err = json.save(data, json_filepath)
if not ok then
-- 调试用输出,生产环境需要可以关掉
print("save label json failed:", save_err)
end

-- 无输出,脚本结束
  • 脚本逻辑:

    1. 尝试读取已有 base_name.json;如不存在或解析失败,则创建新的 data = { shapes = {} }

    2. 始终更新:

      • data.imagePath = imagePath
      • data.imageHeight = imageHeight
      • data.imageWidth = imageWidth
    3. 在追加前,检查 shapes 中是否存在完全相同的 shape(比较 labelshape_type 以及两点坐标);如果已存在则不重复写入。

    4. 若不存在,则 table.insert(shapes, current_shape) 追加一个新标注。

    5. 使用 dkjson.encode(data, { indent = true }) 生成带缩进的 JSON 文本,并覆盖写回 base_name.json

  • 以此实现:

    • 同一工件/时间下的多个缺陷框可以追加到同一个 JSON 文件中;
    • 多次对同一位置重复点击不会新增重复的 shape 记录。

操作流程(运行界面)

下面展示操作员在生产过程中的实际使用步骤,并配套流程图帮助理解整体动作流。

操作流程图

操作步骤说明

运行界面

  1. 等待上位机发送触发信号 产线控制系统发送:

    START,SN12345!

    系统自动解析 SN12345 并进入拍照流程。

  2. 系统自动拍照并展示图像

    左侧显示原图,右侧显示克隆图;矩形区域作为可拖拽标注框。

  3. 操作员检查工件是否存在缺陷

    • 若无缺陷 → 停止操作,等待下一件工件。
    • 若发现缺陷 → 进入下一步。
  4. 调整矩形区域

    拖拽矩形框,使其完整覆盖缺陷区域。

  5. 点击对应缺陷类型按钮

    • 划伤 → 点击「划伤保存」
    • 磕碰 → 点击「磕碰保存」
  6. 系统自动记录 JSON 标注 脚本会根据当前:

    • SN 号;
    • 时间(参与生成 base_name);
    • label(缺陷类型字符串,例如 "impact_crack");
    • 矩形框坐标(x1,y1,x2,y2);
    • 图像尺寸(imageHeight、imageWidth);

    将内容写入:

    本地磁盘:\**\****\******\****\SN_时间.json

    若文件已存在则在 shapes 数组中追加一个新的 shape,不会写入重复条目。

  7. 继续下一件工件 操作员等待新的 START 指令并重复以上流程即可。

同一次拍摄的多次标注

  • 上位机 没有再次发送 START 指令前,同一张图像一直有效,操作员可以在这张图上反复标注。

  • 典型用法:

    1. 在第一个缺陷位置调整矩形区域 → 点击相应缺陷按钮(如「划伤保存」),在 shapes 中写入第一条 shape
    2. 将矩形区域拖到第二个缺陷位置 → 再次点击对应缺陷按钮,脚本会在同一个 SN_时间.json 文件的 shapes 数组末尾追加第二个 shape
    3. 如此循环,可对同一张图上的多个缺陷进行多次标注,无需重新拍照。
  • 标注脚本始终以 同一个 SN + time 生成的 base_name 作为文件名键,只要 SN 和时间不变,该工件的所有缺陷都会累积到同一个 JSON 标注文件中。