停车场场景图像标注 JSON 格式解析与训练模型选择

标注 JSON 结构

自动泊车停车场场景的图像标注通常以折线形式描述结构要素,典型 JSON 结构如下:

{
  "code": 0,
  "data": {
    "markData": {
      "width": 1920,
      "height": 1536,
      "marks": [
        {
          "type": "line",
          "pselect": "墙",
          "class": {
            "pname": "wall"
          },
          "point": [
            {"x": 117.9, "y": 890.8},
            {"x": 164.4, "y": 824.3},
            {"x": 210.9, "y": 777.4}
          ],
          "attrs": {},
          "markId": "y4u-C3GB3x"
        },
        {
          "type": "line",
          "pselect": "立柱",
          "class": {
            "pname": "pillar"
          },
          "attrs": {
            "P": ["square"]
          },
          "point": [
            {"x": 546.7, "y": 626.9},
            {"x": 590.8, "y": 621.1}
          ],
          "markId": "y4u-CBB2i3"
        }
      ],
      "totalNums": {
        "line": 10,
        "polygon": 0,
        "rect": 0,
        "point": 0
      }
    }
  }
}

关键字段说明:

字段说明
type: "line"折线标注(polyline)
pselect中文类别名(墙、路沿、立柱、其他类)
class.pname英文类别名(wall / kerb / pillar / other)
point折线坐标数组,(x,y) 为像素坐标,原点左上角
attrs.P立柱形状(square=方形柱)
objectId实例 ID,0 表示无实例关联

常见标注类别

类别说明用途
wall(墙)停车场侧墙和隔断边界检测
kerb(路沿)车位和道路边缘导航限制
pillar(立柱)承重柱,通常为方形障碍物避让
other(其他类)无法分类的边缘或遮挡训练时通常忽略

训练模型选择

这类折线标注本质是 Polyline Detection / Vector Map Learning,不是目标检测。

数据量 < 1 万张:SegFormer(推荐)

把折线标注膨胀成 Mask(每类一个像素值),用 SegFormer 做语义分割:

JSON 折线标注

生成 Mask(墙=1, 路沿=2, 立柱=3, 背景=0)

SegFormer 训练

Wall/Pillar/Kerb 分割图

SegFormer 训练门槛低、开源实现成熟,几天内可出可用结果。

从 Mask 提取折线坐标(后处理):

import cv2

# 骨架提取
skeleton = cv2.ximgproc.thinning(mask)

# 轮廓提取
contours, _ = cv2.findContours(skeleton, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 折线拟合(减少点数)
for cnt in contours:
    poly = cv2.approxPolyDP(cnt, epsilon=3, closed=False)
    points = [(int(p[0][0]), int(p[0][1])) for p in poly]

数据量 > 10 万张:MapTR(生产级)

MapTR 直接学习向量化地图,输出的就是折线坐标序列,不需要 Mask 中间步骤:

输入图像
↓ MapTR
{"wall": [[x1,y1],[x2,y2],...], "pillar": [...]}

代价是训练难度高、数据格式转换复杂、对数据量要求更高。

方案对比

方案输出坐标难度适合数据量
SegFormer + 后处理间接< 1 万
LaneATT / LSTR直接数万
MapTR直接> 10 万

OpenCV 半自动辅助标注

标注工具里用 OpenCV 实现”沿已标线方向自动延伸”,可减少 50%~80% 的点击次数:

import cv2
import numpy as np

def predict_next_point(image, points):
    if len(points) < 2:
        return None
    
    last = np.array(points[-1])
    prev = np.array(points[-2])
    
    direction = last - prev
    direction = direction / (np.linalg.norm(direction) + 1e-6)
    
    roi_size = 80
    cx, cy = int(last[0]), int(last[1])
    roi = image[max(0,cy-roi_size):cy+roi_size, max(0,cx-roi_size):cx+roi_size]
    
    edges = cv2.Canny(roi, 50, 150)
    lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=15, minLineLength=10, maxLineGap=5)
    
    if lines is None:
        candidate = last + direction * 30
        return tuple(candidate.astype(int))
    
    best_line = None
    best_score = -1
    
    for line in lines:
        x1, y1, x2, y2 = line[0]
        lv = np.array([x2-x1, y2-y1], dtype=float)
        lv = lv / (np.linalg.norm(lv) + 1e-6)
        score = abs(np.dot(direction, lv))
        if score > best_score:
            best_score = score
            best_line = line[0]
    
    if best_line is not None and best_score > 0.7:
        x1, y1, x2, y2 = best_line
        mid = np.array([cx - roi_size + (x1+x2)//2, cy - roi_size + (y1+y2)//2])
        return tuple(mid.astype(int))
    
    return tuple((last + direction * 30).astype(int))

用户标完前两个点后,鼠标移动时实时预显示下一个预测点,按 Enter 确认。