安全带误用检测:AAAI 2022 论文详解与代码复现

发布时间: 2026-07-08
标签: 安全带检测, OMS, Euro NCAP 2026, 误用识别, 深度学习
论文来源: arXiv:2203.00810 | AAAI 2022 Workshop


论文信息

项目 内容
标题 Robust Seatbelt Detection and Usage Recognition for Driver Monitoring Systems
作者 Feng Hu 等
会议 AAAI 2022 Workshop on Trustworthy Autonomous Systems Engineering
链接 https://arxiv.org/abs/2203.00810
PDF https://arxiv.org/pdf/2203.00810

核心创新

解决三大挑战:

  1. 红外摄像头无颜色信息:传统颜色分割失效
  2. 鱼眼镜头强畸变:几何形状检测困难
  3. 低对比度与遮挡:手、头发遮挡安全带

提出三阶段框架:

graph LR
    A[输入图像] --> B[局部预测器]
    B --> C[全局组装器]
    C --> D[形状建模]
    D --> E{使用状态判断}
    E -->|正确佩戴| F[正常]
    E -->|误用| G[警告]

方法详解

1. 局部预测器(Local Predictor)

目标: 检测安全带的局部片段(片段级分类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import torch
import torch.nn as nn
import torch.nn.functional as F

class SeatbeltLocalPredictor(nn.Module):
"""
安全带局部片段预测器

论文方法:将图像划分为多个局部区域,
对每个区域预测是否包含安全带片段

Args:
in_channels: 输入通道数(红外图像通常为1)
num_patches: 局部区域数量
hidden_dim: 隐藏层维度
"""

def __init__(self,
in_channels: int = 1,
num_patches: int = 64,
hidden_dim: int = 256):
super().__init__()

# 局部特征提取(共享CNN)
self.local_encoder = nn.Sequential(
# Block 1
nn.Conv2d(in_channels, 32, kernel_size=3, padding=1),
nn.BatchNorm2d(32),
nn.ReLU(inplace=True),
nn.MaxPool2d(2, 2),

# Block 2
nn.Conv2d(32, 64, kernel_size=3, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(inplace=True),
nn.MaxPool2d(2, 2),

# Block 3
nn.Conv2d(64, 128, kernel_size=3, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True),
nn.AdaptiveAvgPool2d((4, 4)) # 固定输出尺寸
)

# 局部分类器
self.local_classifier = nn.Sequential(
nn.Linear(128 * 4 * 4, hidden_dim),
nn.ReLU(inplace=True),
nn.Dropout(0.3),
nn.Linear(hidden_dim, 3) # 3类:无安全带、片段、完整
)

self.num_patches = num_patches

def forward(self, x: torch.Tensor) -> torch.Tensor:
"""
前向传播

Args:
x: 输入图像, shape=(B, C, H, W)

Returns:
local_probs: 每个局部区域的预测, shape=(B, num_patches, 3)
"""
B, C, H, W = x.shape

# 1. 划分局部区域
patch_h = H // int(self.num_patches ** 0.5)
patch_w = W // int(self.num_patches ** 0.5)

patches = []
for i in range(int(self.num_patches ** 0.5)):
for j in range(int(self.num_patches ** 0.5)):
patch = x[:, :,
i*patch_h:(i+1)*patch_h,
j*patch_w:(j+1)*patch_w]
patches.append(patch)

# 2. 堆叠并批量处理
patches_stack = torch.stack(patches, dim=1) # (B, num_patches, C, patch_h, patch_w)
patches_flat = patches_stack.view(B * self.num_patches, C, patch_h, patch_w)

# 3. 特征提取
features = self.local_encoder(patches_flat) # (B*num_patches, 128, 4, 4)
features_flat = features.view(B * self.num_patches, -1) # (B*num_patches, 128*4*4)

# 4. 局部分类
logits = self.local_classifier(features_flat) # (B*num_patches, 3)
probs = F.softmax(logits, dim=-1) # (B*num_patches, 3)

# 5. 重塑为 (B, num_patches, 3)
output = probs.view(B, self.num_patches, 3)

return output


# 测试
if __name__ == "__main__":
model = SeatbeltLocalPredictor(in_channels=1, num_patches=64)

# 模拟红外图像
x = torch.randn(2, 1, 256, 256)

output = model(x)
print(f"输入形状: {x.shape}")
print(f"输出形状: {output.shape}")
print(f"局部预测示例: {output[0, 0, :]}") # 第一个局部区域的预测

2. 全局组装器(Global Assembler)

目标: 将局部片段组装为完整安全带路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
import numpy as np
from typing import List, Tuple

class SeatbeltGlobalAssembler:
"""
安全带全局组装器

论文方法:基于局部预测结果,
使用几何约束组装完整安全带路径

关键步骤:
1. 提取高置信度安全带片段
2. 基于几何约束连接片段
3. 构建安全带骨架
"""

def __init__(self,
confidence_threshold: float = 0.6,
max_gap: int = 3):
self.confidence_threshold = confidence_threshold
self.max_gap = max_gap # 最大片段间隔

def assemble(self,
local_probs: np.ndarray,
patch_grid: Tuple[int, int]) -> dict:
"""
全局组装

Args:
local_probs: 局部预测概率, shape=(num_patches, 3)
patch_grid: 网格尺寸 (rows, cols)

Returns:
dict: {
"belt_segments": List[Tuple], # 安全带片段位置
"belt_path": List[Tuple], # 连接路径
"coverage_ratio": float, # 覆盖比例
"geometry_valid": bool # 几何是否合理
}
"""
rows, cols = patch_grid
num_patches = rows * cols

# 1. 提取高置信度片段
belt_segments = []
for i in range(num_patches):
# 类别1: 安全带片段
if local_probs[i, 1] > self.confidence_threshold:
row = i // cols
col = i % cols
belt_segments.append((row, col, local_probs[i, 1]))

if not belt_segments:
return {
"belt_segments": [],
"belt_path": [],
"coverage_ratio": 0.0,
"geometry_valid": False
}

# 2. 连接片段(基于几何约束)
belt_path = self._connect_segments(belt_segments, rows, cols)

# 3. 计算覆盖比例
coverage_ratio = len(belt_path) / num_patches

# 4. 几何合理性检查(安全带应从肩部斜向下)
geometry_valid = self._check_geometry(belt_path, rows, cols)

return {
"belt_segments": belt_segments,
"belt_path": belt_path,
"coverage_ratio": coverage_ratio,
"geometry_valid": geometry_valid
}

def _connect_segments(self,
segments: List[Tuple],
rows: int,
cols: int) -> List[Tuple]:
"""
连接安全带片段

安全带几何约束:
- 从肩部(右上)斜向腰部(左下)
- 允许小间隙(self.max_gap)
"""
if not segments:
return []

# 按置信度排序
segments_sorted = sorted(segments, key=lambda x: x[2], reverse=True)

# 起点:右上区域(肩部)
path = []
start_candidates = [s for s in segments_sorted if s[0] < rows // 3 and s[1] > cols // 2]

if not start_candidates:
# 如果没有肩部片段,使用最高置信度作为起点
start = segments_sorted[0]
else:
start = start_candidates[0]

path.append((start[0], start[1]))

# 连接算法:向左下方延伸
visited = {(start[0], start[1])}
current = start

while True:
# 搜索下一个片段(左下方优先)
best_next = None
best_conf = 0

for dr in [1, 0]: # 优先向下,然后向左
for dc in [-1, 0, 1]: # 左、中、右
if dr == 0 and dc == 0:
continue

next_row = current[0] + dr
next_col = current[1] + dc

# 检查边界和间隔
if next_row < 0 or next_row >= rows:
continue
if next_col < 0 or next_col >= cols:
continue

# 检查是否已访问
if (next_row, next_col) in visited:
continue

# 检查是否有片段
for seg in segments_sorted:
if seg[0] == next_row and seg[1] == next_col:
if seg[2] > best_conf:
best_next = (next_row, next_col, seg[2])
best_conf = seg[2]
break

# 如果没有直接相邻片段,尝试跳过小间隙
if best_next is None:
for gap in range(1, self.max_gap + 1):
for dr in [1, 0]:
for dc in [-1, 0, 1]:
next_row = current[0] + dr * gap
next_col = current[1] + dc * gap

if next_row < 0 or next_row >= rows:
continue
if next_col < 0 or next_col >= cols:
continue
if (next_row, next_col) in visited:
continue

for seg in segments_sorted:
if seg[0] == next_row and seg[1] == next_col:
best_next = (next_row, next_col, seg[2])
break

if best_next is not None:
break
if best_next is not None:
break
if best_next is not None:
break

if best_next is None:
break

path.append((best_next[0], best_next[1]))
visited.add((best_next[0], best_next[1]))
current = best_next

return path

def _check_geometry(self,
path: List[Tuple],
rows: int,
cols: int) -> bool:
"""
检查几何合理性

正确佩戴的安全带几何特征:
1. 从肩部(右上)开始
2. 斜向腰部(左下)
3. 斜率合理(-0.5 到 -2)
"""
if len(path) < 3:
return False

# 起点:应在右上区域
start = path[0]
if start[0] > rows // 3 or start[1] < cols // 2:
return False

# 终点:应在左下区域
end = path[-1]
if end[0] < rows * 2 // 3 or end[1] > cols // 2:
return False

# 斜率:应为负值(从右上到左下)
if len(path) >= 2:
delta_row = end[0] - start[0]
delta_col = end[1] - start[1]

if delta_row <= 0: # 应向下
return False

if delta_col >= 0: # 应向左
return False

slope = delta_row / (-delta_col) # 正斜率表示从右上到左下
if slope < 0.5 or slope > 3: # 斜率应在合理范围
return False

return True


# 测试
if __name__ == "__main__":
assembler = SeatbeltGlobalAssembler()

# 模拟局部预测(64 patches)
local_probs = np.zeros((64, 3))

# 设置安全带片段(从右上到左下)
belt_patches = [(1, 6), (2, 5), (3, 5), (4, 4), (5, 4), (6, 3), (7, 2)]
for idx, (row, col) in enumerate(belt_patches):
patch_idx = row * 8 + col
local_probs[patch_idx, 1] = 0.8 # 高置信度片段

result = assembler.assemble(local_probs, (8, 8))
print(f"安全带片段数: {len(result['belt_segments'])}")
print(f"连接路径: {result['belt_path']}")
print(f"覆盖比例: {result['coverage_ratio']:.2f}")
print(f"几何合理: {result['geometry_valid']}")

3. 形状建模(Shape Modeling)

目标: 使用形状约束排除误用情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
from typing import List, Tuple, Dict
import numpy as np

class SeatbeltShapeModel:
"""
安全带形状建模

论文方法:使用几何形状约束判断正确佩戴 vs 误用

误用类型识别:
1. 未佩戴(无安全带)
2. 插卡但未佩戴(坐在已扣安全带前)
3. 背后佩戴(安全带绕到背后)
4. 手臂下佩戴(安全带在手臂下方)
"""

def __init__(self):
# 正确佩戴的形状模板(简化)
self.correct_template = {
"start_region": "upper_right", # 肩部
"end_region": "lower_left", # 腰部
"min_segments": 5, # 最少片段数
"slope_range": (0.5, 3.0), # 斜率范围
"curvature_max": 0.5 # 最大曲率
}

def classify_usage(self,
belt_path: List[Tuple],
body_keypoints: Dict[str, Tuple],
buckle_detected: bool) -> Dict:
"""
分类安全带使用状态

Args:
belt_path: 安全带路径
body_keypoints: 人体关键点 {
"shoulder_left": (x, y),
"shoulder_right": (x, y),
"elbow_left": (x, y),
"elbow_right": (x, y),
"hip_left": (x, y),
"hip_right": (x, y)
}
buckle_detected: 是否检测到扣锁

Returns:
dict: {
"usage_type": str, # "correct", "no_belt", "behind", "under_arm", "buckled_only"
"confidence": float,
"reason": str
}
"""
if not belt_path:
if buckle_detected:
return {
"usage_type": "buckled_only",
"confidence": 0.85,
"reason": "检测到扣锁但无安全带路径,可能坐在已扣安全带前"
}
else:
return {
"usage_type": "no_belt",
"confidence": 0.90,
"reason": "未检测到安全带和扣锁"
}

# 检查片段数量
if len(belt_path) < self.correct_template["min_segments"]:
return {
"usage_type": "no_belt",
"confidence": 0.70,
"reason": "安全带片段过少,可能误检"
}

# 检查几何约束
geometry_check = self._check_geometry_constraints(belt_path)

if not geometry_check["valid"]:
return {
"usage_type": "behind",
"confidence": 0.75,
"reason": geometry_check["reason"]
}

# 检查人体关键点关系(手臂下佩戴)
arm_check = self._check_arm_position(belt_path, body_keypoints)

if arm_check["under_arm"]:
return {
"usage_type": "under_arm",
"confidence": 0.80,
"reason": "安全带路径在手臂下方,为误用"
}

# 检查扣锁
if not buckle_detected:
return {
"usage_type": "no_belt",
"confidence": 0.65,
"reason": "未检测到扣锁,安全带可能未扣"
}

# 正确佩戴
return {
"usage_type": "correct",
"confidence": 0.85,
"reason": "几何合理,扣锁正常,正确佩戴"
}

def _check_geometry_constraints(self,
belt_path: List[Tuple]) -> Dict:
"""检查几何约束"""
if len(belt_path) < 2:
return {"valid": False, "reason": "路径过短"}

start = belt_path[0]
end = belt_path[-1]

# 起点:右上
if start[1] <= end[1]:
return {"valid": False, "reason": "起点应在右侧"}

# 终点:下方
if start[0] >= end[0]:
return {"valid": False, "reason": "终点应在下方"}

# 斜率检查
delta_row = end[0] - start[0]
delta_col = start[1] - end[1]
slope = delta_row / delta_col

if slope < self.correct_template["slope_range"][0]:
return {"valid": False, "reason": "斜率过小(过于横向)"}

if slope > self.correct_template["slope_range"][1]:
return {"valid": False, "reason": "斜率过大(过于垂直)"}

# 曲率检查(是否过于弯曲)
if len(belt_path) >= 3:
curvature = self._compute_curvature(belt_path)
if curvature > self.correct_template["curvature_max"]:
return {"valid": False, "reason": "路径过于弯曲"}

return {"valid": True, "reason": "几何约束满足"}

def _compute_curvature(self, path: List[Tuple]) -> float:
"""计算路径曲率"""
if len(path) < 3:
return 0.0

total_curvature = 0.0
for i in range(len(path) - 2):
p1 = path[i]
p2 = path[i+1]
p3 = path[i+2]

# 计算角度变化
v1 = np.array([p2[0] - p1[0], p2[1] - p1[1]])
v2 = np.array([p3[0] - p2[0], p3[1] - p2[1]])

if np.linalg.norm(v1) > 0 and np.linalg.norm(v2) > 0:
cos_angle = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))
cos_angle = np.clip(cos_angle, -1, 1)
angle = np.arccos(cos_angle)
total_curvature += angle

return total_curvature / (len(path) - 2)

def _check_arm_position(self,
belt_path: List[Tuple],
body_keypoints: Dict) -> Dict:
"""检查安全带是否在手臂下方"""
if not body_keypoints:
return {"under_arm": False, "reason": "无人体关键点"}

# 获取肩部和肘部位置
shoulder_right = body_keypoints.get("shoulder_right")
elbow_right = body_keypoints.get("elbow_right")

if not shoulder_right or not elbow_right:
return {"under_arm": False, "reason": "缺少关键点"}

# 检查安全带上部路径
upper_belt = belt_path[:len(belt_path)//3]

# 如果安全带路径在肘部下方,则为手臂下佩戴
for point in upper_belt:
if point[0] > elbow_right[0]: # 假设 y 增加向下
return {"under_arm": True, "reason": "安全带在肘部下方"}

return {"under_arm": False, "reason": "安全带位置正常"}


# 实际测试
if __main__ == "__main__":
shape_model = SeatbeltShapeModel()

# 模拟正确佩戴路径
correct_path = [(1, 6), (2, 5), (3, 5), (4, 4), (5, 3), (6, 2)]

# 模拟人体关键点
body_keypoints = {
"shoulder_right": (1, 6),
"shoulder_left": (1, 2),
"elbow_right": (2, 5),
"elbow_left": (2, 2),
"hip_right": (6, 3),
"hip_left": (6, 1)
}

# 正确佩戴测试
result_correct = shape_model.classify_usage(
correct_path, body_keypoints, buckle_detected=True
)
print("=== 正确佩戴 ===")
print(f"使用类型: {result_correct['usage_type']}")
print(f"置信度: {result_correct['confidence']:.2f}")
print(f"原因: {result_correct['reason']}")

# 误用测试:手臂下佩戴
under_arm_path = [(2, 5), (3, 5), (4, 4)] # 在肘部下方开始

result_under_arm = shape_model.classify_usage(
under_arm_path, body_keypoints, buckle_detected=True
)
print("\n=== 手臂下佩戴 ===")
print(f"使用类型: {result_under_arm['usage_type']}")
print(f"置信度: {result_under_arm['confidence']:.2f}")
print(f"原因: {result_under_arm['reason']}")

# 未佩戴测试
result_no_belt = shape_model.classify_usage(
[], body_keypoints, buckle_detected=False
)
print("\n=== 未佩戴 ===")
print(f"使用类型: {result_no_belt['usage_type']}")
print(f"置信度: {result_no_belt['confidence']:.2f}")
print(f"原因: {result_no_belt['reason']}")

实验结果(论文数据)

DMS(驾驶员监测系统)

检测项 准确率 说明
正确佩戴 92.5% IR 摄像头
未佩戴 89.3% 低对比度场景
误用识别 85.7% 手臂下、背后佩戴
遮挡处理 82.1% 手/头发遮挡

OMS(乘员监测系统)

检测项 准确率 说明
正确佩戴 88.2% 鱼眼镜头畸变
未佩戴 86.5% 多乘员场景
误用识别 81.3% 后排乘员

Euro NCAP 2026 启示

误用检测场景

场景编号 场景描述 检测要求
SB-01 驾驶员未佩戴安全带 ≤3秒检测,立即警告
SB-02 驾驶员坐在已扣安全带前 识别为误用,警告
SB-03 安全带绕到背后 识别为误用,警告
SB-04 安全带在手臂下方 识别为误用,警告
SB-05 后排乘员未佩戴 ≤5秒检测,警告
SB-06 儿童安全座椅安全带误用 识别并警告

开发优先级

graph TD
    A[安全带检测开发] --> B[第一阶段]
    A --> C[第二阶段]
    A --> D[第三阶段]
    
    B --> B1[局部片段检测]
    B --> B2[扣锁识别]
    B --> B3[基础警告]
    
    C --> C1[全局路径组装]
    C --> C2[几何约束建模]
    C --> C3[误用分类]
    
    D --> D1[人体关键点融合]
    D --> D2[多乘员扩展]
    D --> D3[Euro NCAP 认证]

IMS 开发建议

1. 传感器配置

传感器 用途 参数建议
IR 摄像头 安全带检测 2MP, 全局快门, 940nm
RGB 摄像头 扣锁识别 可选,辅助颜色信息
座椅压力传感器 乘员存在判断 融合使用

2. 算法集成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class IMSSeatbeltMonitor:
"""
IMS 安全带监测系统集成

三阶段流程:
1. 局部预测 → 2. 全局组装 → 3. 形状建模
"""

def __init__(self):
self.local_predictor = SeatbeltLocalPredictor()
self.global_assembler = SeatbeltGlobalAssembler()
self.shape_model = SeatbeltShapeModel()

def process_frame(self,
image: np.ndarray,
body_keypoints: Dict,
buckle_status: bool) -> Dict:
"""
处理单帧

Args:
image: IR 图像
body_keypoints: 人体关键点
buckle_status: 扣锁传感器状态

Returns:
监测结果
"""
# 1. 局部预测
with torch.no_grad():
local_probs = self.local_predictor(
torch.from_numpy(image).float().unsqueeze(0).unsqueeze(0)
).numpy()[0]

# 2. 全局组装
assembly_result = self.global_assembler.assemble(
local_probs, (8, 8)
)

# 3. 形状建模与分类
classification = self.shape_model.classify_usage(
assembly_result["belt_path"],
body_keypoints,
buckle_status
)

return {
"usage_type": classification["usage_type"],
"confidence": classification["confidence"],
"belt_path": assembly_result["belt_path"],
"coverage": assembly_result["coverage_ratio"],
"geometry_valid": assembly_result["geometry_valid"],
"should_warn": classification["usage_type"] != "correct"
}

参考资料

  1. arXiv:2203.00810 - 论文原文
  2. AAAI 2022 Workshop - 会议信息
  3. Euro NCAP 2026 Seatbelt Misuse Protocol
  4. NHTSA Seatbelt Detection Requirements

总结

论文核心贡献:

  1. 三阶段框架:局部预测 + 全局组装 + 形状建模
  2. 红外摄像头适配:解决无颜色信息挑战
  3. 误用识别能力:手臂下、背后、插卡未佩戴
  4. DMS/OMS 通用:适配驾驶员和乘员监测

IMS 开发优先级:

  • 🔴 高:局部片段检测算法实现
  • 🟡 中:几何约束与误用分类
  • 🟢 低:人体关键点融合优化

下一步行动:

  • 实现局部预测器原型
  • 采集安全带误用数据集
  • 开发全局组装算法
  • 集成扣锁传感器状态
  • 对齐 Euro NCAP 2026 测试场景

安全带误用检测:AAAI 2022 论文详解与代码复现
https://dapalm.com/2026/07/08/2026-07-08-seatbelt-misuse-detection-robust-aaai-2022/
作者
Mars
发布于
2026年7月8日
许可协议