PDAson commited on
Commit
b6d2710
·
verified ·
1 Parent(s): f6a5872

Update annotator_v3_3.py

Browse files
Files changed (1) hide show
  1. annotator_v3_3.py +380 -378
annotator_v3_3.py CHANGED
@@ -1,378 +1,380 @@
1
- import gradio as gr
2
- import json
3
- import os
4
- import re
5
- from collections import defaultdict
6
-
7
- def load_main_data(json_file_path):
8
- """假设 JSON 是一个列表,每个元素形如:
9
- {
10
- 'image_path': 'some_path',
11
- 'subject': 'xxx',
12
- 'object': 'yyy',
13
- 'options': { 'state': [...], 'action': [...], 'spatial': [...] }
14
- }
15
- """
16
- with open(json_file_path, 'r', encoding='utf-8') as f:
17
- return json.load(f)
18
-
19
- def load_output_dict(output_file):
20
- """读取已标注数据。如果不存在,则返回空字典。"""
21
- if os.path.exists(output_file):
22
- try:
23
- with open(output_file, 'r', encoding='utf-8') as f:
24
- data = json.load(f)
25
- if not isinstance(data, dict):
26
- data = {}
27
- return data
28
- except json.JSONDecodeError:
29
- return {}
30
- else:
31
- return {}
32
-
33
- def save_output_dict(output_file, data):
34
- """保存标注结果到 output.json"""
35
- with open(output_file, 'w', encoding='utf-8') as f:
36
- json.dump(data, f, indent=2, ensure_ascii=False)
37
-
38
- def extract_image_id_from_path(full_path):
39
- """
40
- 从 full_path 提取 'image_数字' 这一段,若找不到则返回去掉目录的文件名。
41
- 例如:
42
- annotated_image_folder\\split_4\\output_images_1592503\\image_1592503_pair_2_black bus_parked on.jpg
43
- -> 'image_1592503'
44
- """
45
- # 先统一斜杠
46
- full_path = full_path.replace("\\", "/")
47
- filename = os.path.basename(full_path)
48
- # 用正则匹配 "image_后面若干数字"
49
- m = re.search(r"(image_\d+)", filename)
50
- if m:
51
- return m.group(1)
52
- return filename # 如果失败,就退而求其次(不建议这么多文件都失败)
53
-
54
- def gradio_interface(json_file_path='sample_six_grid_split_4_new.json'):
55
- """
56
- 主要变化:
57
- 1) 用 extract_image_id_from_path 提取 image_XXXX 做分组,以便同一原图的多个 pair 正确显示 "Pair x/y for this image"。
58
- 2) 保留 Subject / Object 并排显示,并在 status 中额外显示:此 pair 在当前图片中是第几/共几。
59
- """
60
-
61
- data = load_main_data(json_file_path) # 假设是 list
62
- output_file = 'output.json'
63
- labeled_data = load_output_dict(output_file)
64
-
65
- # ---------------------------------------------------
66
- # 1) 预处理:根据 "image_id" 分组
67
- # ---------------------------------------------------
68
- image_to_indices = defaultdict(list)
69
- for idx, item in enumerate(data):
70
- raw_path = item.get("image_path", "")
71
- image_id = extract_image_id_from_path(raw_path)
72
- image_to_indices[image_id].append(idx)
73
-
74
- local_index_map = {}
75
- local_count_map = {}
76
- for image_id, idx_list in image_to_indices.items():
77
- # 保持出现顺序
78
- for local_i, real_idx in enumerate(idx_list):
79
- local_index_map[real_idx] = local_i
80
- local_count_map[real_idx] = len(idx_list)
81
-
82
- # ---------------------------------------------------
83
- # 2) 一些辅助函数
84
- # ---------------------------------------------------
85
- def get_item_info(idx):
86
- item = data[idx]
87
- image_path = item.get("image_path", "")
88
- if not os.path.exists(image_path):
89
- image_path = "placeholder.jpg"
90
- subject = item.get("subject", "")
91
- obj = item.get("object", "")
92
- opts = item.get("options", {})
93
- return image_path, subject, obj, opts
94
-
95
- def split_options(options_list):
96
- """前5个给 Radio,其余给 Dropdown"""
97
- if len(options_list) <= 5:
98
- return options_list, []
99
- else:
100
- return options_list[:5], options_list[5:]
101
-
102
- def update_final_selection(radio_val, dropdown_val):
103
- """Radio 优先,否则 Dropdown"""
104
- if radio_val:
105
- return radio_val
106
- return dropdown_val or None
107
-
108
- def update_skip_value(checked):
109
- """skip_checkbox => bool -> str"""
110
- return str(checked)
111
-
112
- # ---------------------------------------------------
113
- # 3) 初始化:idx=0
114
- # ---------------------------------------------------
115
- init_idx = 0
116
- init_image, init_sub, init_obj, init_opts = get_item_info(init_idx)
117
- state_radio_list, state_dropdown_list = split_options(init_opts.get("state", []))
118
- action_radio_list, action_dropdown_list = split_options(init_opts.get("action", []))
119
- spatial_radio_list, spatial_dropdown_list = split_options(init_opts.get("spatial", []))
120
-
121
- init_radio_val = None
122
- init_dropdown_val = None
123
- init_skip_val = False
124
-
125
- # ---------------------------------------------------
126
- # 4) 搭建 Gradio 界面
127
- # ---------------------------------------------------
128
- with gr.Blocks() as demo:
129
- cur_idx_state = gr.State(init_idx)
130
-
131
- with gr.Row():
132
- # 左侧:图像、Status、Details,以及翻���按钮
133
- with gr.Column(scale=1):
134
- img_view = gr.Image(value=init_image, label="Image")
135
-
136
- # 这里的 status_box 会显示 全局进度+当前图片内的进度
137
- status_box = gr.Textbox(
138
- value="",
139
- label="Status",
140
- interactive=False
141
- )
142
- info_box = gr.Textbox(
143
- value="Details: (will be updated...)",
144
- label="Details",
145
- interactive=False
146
- )
147
- with gr.Row():
148
- btn_prev = gr.Button("← Previous", variant="secondary")
149
- btn_next = gr.Button("Next →", variant="primary")
150
-
151
- # 右侧:主逻辑
152
- with gr.Column(scale=1):
153
- # 在同一个 Row 显示 (Subject -> Object) + skip_checkbox
154
- with gr.Row():
155
- subject_object_md = gr.Markdown(
156
- f"**{init_sub} → {init_obj}**",
157
- elem_id="subject_object_header"
158
- )
159
- skip_checkbox = gr.Checkbox(
160
- value=init_skip_val,
161
- label="No relation (skip this pair)"
162
- )
163
- skip_final = gr.Textbox(value=str(init_skip_val), visible=False)
164
- skip_checkbox.change(
165
- fn=update_skip_value,
166
- inputs=[skip_checkbox],
167
- outputs=[skip_final]
168
- )
169
-
170
- # --- State ---
171
- gr.Markdown("### State")
172
- state_radio = gr.Radio(choices=state_radio_list, value=init_radio_val, label="Top 5")
173
- state_dd = gr.Dropdown(choices=state_dropdown_list, value=init_dropdown_val, label="More Options")
174
- state_final = gr.Textbox(value=None, visible=False, label="Final State")
175
-
176
- state_radio.change(
177
- fn=update_final_selection,
178
- inputs=[state_radio, state_dd],
179
- outputs=state_final
180
- )
181
- state_dd.change(
182
- fn=update_final_selection,
183
- inputs=[state_radio, state_dd],
184
- outputs=state_final
185
- )
186
-
187
- # --- Action ---
188
- gr.Markdown("### Action")
189
- action_radio = gr.Radio(choices=action_radio_list, value=init_radio_val, label="Top 5")
190
- action_dd = gr.Dropdown(choices=action_dropdown_list, value=init_dropdown_val, label="More Options")
191
- action_final = gr.Textbox(value=None, visible=False, label="Final Action")
192
-
193
- action_radio.change(
194
- fn=update_final_selection,
195
- inputs=[action_radio, action_dd],
196
- outputs=action_final
197
- )
198
- action_dd.change(
199
- fn=update_final_selection,
200
- inputs=[action_radio, action_dd],
201
- outputs=action_final
202
- )
203
-
204
- # --- Spatial ---
205
- gr.Markdown("### Spatial")
206
- spatial_radio = gr.Radio(choices=spatial_radio_list, value=init_radio_val, label="Top 5")
207
- spatial_dd = gr.Dropdown(choices=spatial_dropdown_list, value=init_dropdown_val, label="More Options")
208
- spatial_final = gr.Textbox(value=None, visible=False, label="Final Spatial")
209
-
210
- spatial_radio.change(
211
- fn=update_final_selection,
212
- inputs=[spatial_radio, spatial_dd],
213
- outputs=spatial_final
214
- )
215
- spatial_dd.change(
216
- fn=update_final_selection,
217
- inputs=[spatial_radio, spatial_dd],
218
- outputs=spatial_final
219
- )
220
-
221
- # 底部的 Save
222
- with gr.Row():
223
- btn_save = gr.Button("Save", variant="primary")
224
-
225
- # ---------------------------------------------------
226
- # 5) 翻页函数
227
- # ---------------------------------------------------
228
- def go_next(cur_idx):
229
- new_idx = (cur_idx + 1) % len(data)
230
- return _jump_to_index(new_idx)
231
-
232
- def go_prev(cur_idx):
233
- new_idx = (cur_idx - 1) % len(data)
234
- return _jump_to_index(new_idx)
235
-
236
- def _jump_to_index(new_idx):
237
- # 获取数据
238
- image_path, sub, obj, opts = get_item_info(new_idx)
239
- # 全局进度:new_idx+1 / len(data)
240
- global_status = f"Currently showing: {new_idx+1}/{len(data)}"
241
-
242
- # 获取本图的局部索引
243
- local_idx = local_index_map[new_idx] # 从 0 开始
244
- local_count = local_count_map[new_idx]
245
- # 组合显示
246
- new_status = f"{global_status}. (Pair {local_idx+1}/{local_count} for this image.)"
247
-
248
- new_info = f"Subject: {sub}, Object: {obj}"
249
- # 改 Markdown: "**sub -> obj**"
250
- subobj_md = f"**{sub} {obj}**"
251
-
252
- st_list, st_dd = split_options(opts.get("state", []))
253
- ac_list, ac_dd = split_options(opts.get("action", []))
254
- sp_list, sp_dd = split_options(opts.get("spatial", []))
255
-
256
- rec = labeled_data.get(str(new_idx), {})
257
- skip_val = rec.get("skip", False)
258
- if skip_val is True:
259
- final_st_val = None
260
- final_ac_val = None
261
- final_sp_val = None
262
- else:
263
- final_st_val = rec.get("state", None)
264
- final_ac_val = rec.get("action", None)
265
- final_sp_val = rec.get("spatial", None)
266
-
267
- return (
268
- # 更新索引
269
- new_idx,
270
- # 更新图像
271
- image_path,
272
- # 更新 Status, Info
273
- new_status,
274
- new_info,
275
- # 更新 subject_object_md
276
- subobj_md,
277
- # skip
278
- bool(skip_val),
279
- str(skip_val),
280
- # state
281
- gr.update(choices=st_list, value=None),
282
- gr.update(choices=st_dd, value=None),
283
- final_st_val,
284
- # action
285
- gr.update(choices=ac_list, value=None),
286
- gr.update(choices=ac_dd, value=None),
287
- final_ac_val,
288
- # spatial
289
- gr.update(choices=sp_list, value=None),
290
- gr.update(choices=sp_dd, value=None),
291
- final_sp_val
292
- )
293
-
294
- btn_next.click(
295
- fn=go_next,
296
- inputs=[cur_idx_state],
297
- outputs=[
298
- cur_idx_state,
299
- img_view,
300
- status_box,
301
- info_box,
302
- subject_object_md,
303
- skip_checkbox,
304
- skip_final,
305
- state_radio,
306
- state_dd,
307
- state_final,
308
- action_radio,
309
- action_dd,
310
- action_final,
311
- spatial_radio,
312
- spatial_dd,
313
- spatial_final
314
- ]
315
- )
316
-
317
- btn_prev.click(
318
- fn=go_prev,
319
- inputs=[cur_idx_state],
320
- outputs=[
321
- cur_idx_state,
322
- img_view,
323
- status_box,
324
- info_box,
325
- subject_object_md,
326
- skip_checkbox,
327
- skip_final,
328
- state_radio,
329
- state_dd,
330
- state_final,
331
- action_radio,
332
- action_dd,
333
- action_final,
334
- spatial_radio,
335
- spatial_dd,
336
- spatial_final
337
- ]
338
- )
339
-
340
- # ---------------------------------------------------
341
- # 6) 保存逻辑
342
- # ---------------------------------------------------
343
- def handle_save(st_val, ac_val, sp_val, cur_idx, skip_val):
344
- skip_flag = (skip_val == "True")
345
- image_path, sub, obj, _ = get_item_info(cur_idx)
346
- if skip_flag:
347
- labeled_data[str(cur_idx)] = {
348
- "subject": sub,
349
- "object": obj,
350
- "skip": True
351
- }
352
- save_output_dict(output_file, labeled_data)
353
- return f"Skipped pair: {sub} - {obj}."
354
- else:
355
- if not st_val or not ac_val or not sp_val:
356
- return "Please select all 3 categories or check 'no suitable option'!"
357
- labeled_data[str(cur_idx)] = {
358
- "subject": sub,
359
- "object": obj,
360
- "skip": False,
361
- "state": st_val,
362
- "action": ac_val,
363
- "spatial": sp_val
364
- }
365
- save_output_dict(output_file, labeled_data)
366
- return f"Saved: {sub}, {obj}, state={st_val}, action={ac_val}, spatial={sp_val}"
367
-
368
- btn_save.click(
369
- fn=handle_save,
370
- inputs=[state_final, action_final, spatial_final, cur_idx_state, skip_final],
371
- outputs=status_box
372
- )
373
-
374
- return demo
375
-
376
-
377
- if __name__ == '__main__':
378
- gradio_interface().launch(share=True)
 
 
 
1
+ import gradio as gr
2
+ import json
3
+ import os
4
+ import re
5
+ from collections import defaultdict
6
+
7
+ def load_main_data(json_file_path):
8
+ """假设 JSON 是一个列表,每个元素形如:
9
+ {
10
+ 'image_path': 'some_path',
11
+ 'subject': 'xxx',
12
+ 'object': 'yyy',
13
+ 'options': { 'state': [...], 'action': [...], 'spatial': [...] }
14
+ }
15
+ """
16
+ with open(json_file_path, 'r', encoding='utf-8') as f:
17
+ return json.load(f)
18
+
19
+ def load_output_dict(output_file):
20
+ """读取已标注数据。如果不存在,则返回空字典。"""
21
+ if os.path.exists(output_file):
22
+ try:
23
+ with open(output_file, 'r', encoding='utf-8') as f:
24
+ data = json.load(f)
25
+ if not isinstance(data, dict):
26
+ data = {}
27
+ return data
28
+ except json.JSONDecodeError:
29
+ return {}
30
+ else:
31
+ return {}
32
+
33
+ def save_output_dict(output_file, data):
34
+ """保存标注结果到 output.json"""
35
+ with open(output_file, 'w', encoding='utf-8') as f:
36
+ json.dump(data, f, indent=2, ensure_ascii=False)
37
+
38
+ def extract_image_id_from_path(full_path):
39
+ """
40
+ 从 full_path 提取 'image_数字' 这一段,若找不到则返回去掉目录的文件名。
41
+ 例如:
42
+ annotated_image_folder\\split_4\\output_images_1592503\\image_1592503_pair_2_black bus_parked on.jpg
43
+ -> 'image_1592503'
44
+ """
45
+ # 先统一斜杠
46
+ full_path = full_path.replace("\\", "/")
47
+ filename = os.path.basename(full_path)
48
+ # 用正则匹配 "image_后面若干数字"
49
+ m = re.search(r"(image_\d+)", filename)
50
+ if m:
51
+ return m.group(1)
52
+ return filename # 如果失败,就退而求其次(不建议这么多文件都失败)
53
+
54
+ def gradio_interface(json_file_path='sample_six_grid_split_4_new.json'):
55
+ """
56
+ 主要变化:
57
+ 1) 用 extract_image_id_from_path 提取 image_XXXX 做分组,以便同一原图的多个 pair 正确显示 "Pair x/y for this image"。
58
+ 2) 保留 Subject / Object 并排显示,并在 status 中额外显示:此 pair 在当前图片中是第几/共几。
59
+ """
60
+
61
+ data = load_main_data(json_file_path) # 假设是 list
62
+ output_file = 'output.json'
63
+ labeled_data = load_output_dict(output_file)
64
+
65
+ # ---------------------------------------------------
66
+ # 1) 预处理:根据 "image_id" 分组
67
+ # ---------------------------------------------------
68
+ image_to_indices = defaultdict(list)
69
+ for idx, item in enumerate(data):
70
+ raw_path = item.get("image_path", "")
71
+ image_id = extract_image_id_from_path(raw_path)
72
+ image_to_indices[image_id].append(idx)
73
+
74
+ local_index_map = {}
75
+ local_count_map = {}
76
+ for image_id, idx_list in image_to_indices.items():
77
+ # 保持出现顺序
78
+ for local_i, real_idx in enumerate(idx_list):
79
+ local_index_map[real_idx] = local_i
80
+ local_count_map[real_idx] = len(idx_list)
81
+
82
+ # ---------------------------------------------------
83
+ # 2) 一些辅助函数
84
+ # ---------------------------------------------------
85
+ def get_item_info(idx):
86
+ item = data[idx]
87
+ image_path = item.get("image_path", "")
88
+ print(f"The current image_path is {image_path}")
89
+
90
+ if not os.path.exists(image_path):
91
+ image_path = "placeholder.jpg"
92
+ subject = item.get("subject", "")
93
+ obj = item.get("object", "")
94
+ opts = item.get("options", {})
95
+ return image_path, subject, obj, opts
96
+
97
+ def split_options(options_list):
98
+ """前5个给 Radio,其余给 Dropdown"""
99
+ if len(options_list) <= 5:
100
+ return options_list, []
101
+ else:
102
+ return options_list[:5], options_list[5:]
103
+
104
+ def update_final_selection(radio_val, dropdown_val):
105
+ """Radio 优先,否则 Dropdown"""
106
+ if radio_val:
107
+ return radio_val
108
+ return dropdown_val or None
109
+
110
+ def update_skip_value(checked):
111
+ """skip_checkbox => bool -> str"""
112
+ return str(checked)
113
+
114
+ # ---------------------------------------------------
115
+ # 3) 初始化:idx=0
116
+ # ---------------------------------------------------
117
+ init_idx = 0
118
+ init_image, init_sub, init_obj, init_opts = get_item_info(init_idx)
119
+ state_radio_list, state_dropdown_list = split_options(init_opts.get("state", []))
120
+ action_radio_list, action_dropdown_list = split_options(init_opts.get("action", []))
121
+ spatial_radio_list, spatial_dropdown_list = split_options(init_opts.get("spatial", []))
122
+
123
+ init_radio_val = None
124
+ init_dropdown_val = None
125
+ init_skip_val = False
126
+
127
+ # ---------------------------------------------------
128
+ # 4) 搭建 Gradio 界面
129
+ # ---------------------------------------------------
130
+ with gr.Blocks() as demo:
131
+ cur_idx_state = gr.State(init_idx)
132
+
133
+ with gr.Row():
134
+ # 左侧:图像、Status、Details,以及翻页按钮
135
+ with gr.Column(scale=1):
136
+ img_view = gr.Image(value=init_image, label="Image")
137
+
138
+ # 这里的 status_box 会显示 全局进度+当前图片内的进度
139
+ status_box = gr.Textbox(
140
+ value="",
141
+ label="Status",
142
+ interactive=False
143
+ )
144
+ info_box = gr.Textbox(
145
+ value="Details: (will be updated...)",
146
+ label="Details",
147
+ interactive=False
148
+ )
149
+ with gr.Row():
150
+ btn_prev = gr.Button("← Previous", variant="secondary")
151
+ btn_next = gr.Button("Next →", variant="primary")
152
+
153
+ # 右侧:主逻辑
154
+ with gr.Column(scale=1):
155
+ # 在同一个 Row 显示 (Subject -> Object) + skip_checkbox
156
+ with gr.Row():
157
+ subject_object_md = gr.Markdown(
158
+ f"**{init_sub} → {init_obj}**",
159
+ elem_id="subject_object_header"
160
+ )
161
+ skip_checkbox = gr.Checkbox(
162
+ value=init_skip_val,
163
+ label="No relation (skip this pair)"
164
+ )
165
+ skip_final = gr.Textbox(value=str(init_skip_val), visible=False)
166
+ skip_checkbox.change(
167
+ fn=update_skip_value,
168
+ inputs=[skip_checkbox],
169
+ outputs=[skip_final]
170
+ )
171
+
172
+ # --- State ---
173
+ gr.Markdown("### State")
174
+ state_radio = gr.Radio(choices=state_radio_list, value=init_radio_val, label="Top 5")
175
+ state_dd = gr.Dropdown(choices=state_dropdown_list, value=init_dropdown_val, label="More Options")
176
+ state_final = gr.Textbox(value=None, visible=False, label="Final State")
177
+
178
+ state_radio.change(
179
+ fn=update_final_selection,
180
+ inputs=[state_radio, state_dd],
181
+ outputs=state_final
182
+ )
183
+ state_dd.change(
184
+ fn=update_final_selection,
185
+ inputs=[state_radio, state_dd],
186
+ outputs=state_final
187
+ )
188
+
189
+ # --- Action ---
190
+ gr.Markdown("### Action")
191
+ action_radio = gr.Radio(choices=action_radio_list, value=init_radio_val, label="Top 5")
192
+ action_dd = gr.Dropdown(choices=action_dropdown_list, value=init_dropdown_val, label="More Options")
193
+ action_final = gr.Textbox(value=None, visible=False, label="Final Action")
194
+
195
+ action_radio.change(
196
+ fn=update_final_selection,
197
+ inputs=[action_radio, action_dd],
198
+ outputs=action_final
199
+ )
200
+ action_dd.change(
201
+ fn=update_final_selection,
202
+ inputs=[action_radio, action_dd],
203
+ outputs=action_final
204
+ )
205
+
206
+ # --- Spatial ---
207
+ gr.Markdown("### Spatial")
208
+ spatial_radio = gr.Radio(choices=spatial_radio_list, value=init_radio_val, label="Top 5")
209
+ spatial_dd = gr.Dropdown(choices=spatial_dropdown_list, value=init_dropdown_val, label="More Options")
210
+ spatial_final = gr.Textbox(value=None, visible=False, label="Final Spatial")
211
+
212
+ spatial_radio.change(
213
+ fn=update_final_selection,
214
+ inputs=[spatial_radio, spatial_dd],
215
+ outputs=spatial_final
216
+ )
217
+ spatial_dd.change(
218
+ fn=update_final_selection,
219
+ inputs=[spatial_radio, spatial_dd],
220
+ outputs=spatial_final
221
+ )
222
+
223
+ # 底部的 Save
224
+ with gr.Row():
225
+ btn_save = gr.Button("Save", variant="primary")
226
+
227
+ # ---------------------------------------------------
228
+ # 5) 翻页函数
229
+ # ---------------------------------------------------
230
+ def go_next(cur_idx):
231
+ new_idx = (cur_idx + 1) % len(data)
232
+ return _jump_to_index(new_idx)
233
+
234
+ def go_prev(cur_idx):
235
+ new_idx = (cur_idx - 1) % len(data)
236
+ return _jump_to_index(new_idx)
237
+
238
+ def _jump_to_index(new_idx):
239
+ # 获取数据
240
+ image_path, sub, obj, opts = get_item_info(new_idx)
241
+ # 全局进度:new_idx+1 / len(data)
242
+ global_status = f"Currently showing: {new_idx+1}/{len(data)}"
243
+
244
+ # 获取本图的局部索引
245
+ local_idx = local_index_map[new_idx] # 从 0 开始
246
+ local_count = local_count_map[new_idx]
247
+ # 组合显示
248
+ new_status = f"{global_status}. (Pair {local_idx+1}/{local_count} for this image.)"
249
+
250
+ new_info = f"Subject: {sub}, Object: {obj}"
251
+ # 改 Markdown: "**sub -> obj**"
252
+ subobj_md = f"**{sub} → {obj}**"
253
+
254
+ st_list, st_dd = split_options(opts.get("state", []))
255
+ ac_list, ac_dd = split_options(opts.get("action", []))
256
+ sp_list, sp_dd = split_options(opts.get("spatial", []))
257
+
258
+ rec = labeled_data.get(str(new_idx), {})
259
+ skip_val = rec.get("skip", False)
260
+ if skip_val is True:
261
+ final_st_val = None
262
+ final_ac_val = None
263
+ final_sp_val = None
264
+ else:
265
+ final_st_val = rec.get("state", None)
266
+ final_ac_val = rec.get("action", None)
267
+ final_sp_val = rec.get("spatial", None)
268
+
269
+ return (
270
+ # 更新索引
271
+ new_idx,
272
+ # 更新图像
273
+ image_path,
274
+ # 更新 Status, Info
275
+ new_status,
276
+ new_info,
277
+ # 更新 subject_object_md
278
+ subobj_md,
279
+ # skip
280
+ bool(skip_val),
281
+ str(skip_val),
282
+ # state
283
+ gr.update(choices=st_list, value=None),
284
+ gr.update(choices=st_dd, value=None),
285
+ final_st_val,
286
+ # action
287
+ gr.update(choices=ac_list, value=None),
288
+ gr.update(choices=ac_dd, value=None),
289
+ final_ac_val,
290
+ # spatial
291
+ gr.update(choices=sp_list, value=None),
292
+ gr.update(choices=sp_dd, value=None),
293
+ final_sp_val
294
+ )
295
+
296
+ btn_next.click(
297
+ fn=go_next,
298
+ inputs=[cur_idx_state],
299
+ outputs=[
300
+ cur_idx_state,
301
+ img_view,
302
+ status_box,
303
+ info_box,
304
+ subject_object_md,
305
+ skip_checkbox,
306
+ skip_final,
307
+ state_radio,
308
+ state_dd,
309
+ state_final,
310
+ action_radio,
311
+ action_dd,
312
+ action_final,
313
+ spatial_radio,
314
+ spatial_dd,
315
+ spatial_final
316
+ ]
317
+ )
318
+
319
+ btn_prev.click(
320
+ fn=go_prev,
321
+ inputs=[cur_idx_state],
322
+ outputs=[
323
+ cur_idx_state,
324
+ img_view,
325
+ status_box,
326
+ info_box,
327
+ subject_object_md,
328
+ skip_checkbox,
329
+ skip_final,
330
+ state_radio,
331
+ state_dd,
332
+ state_final,
333
+ action_radio,
334
+ action_dd,
335
+ action_final,
336
+ spatial_radio,
337
+ spatial_dd,
338
+ spatial_final
339
+ ]
340
+ )
341
+
342
+ # ---------------------------------------------------
343
+ # 6) 保存逻辑
344
+ # ---------------------------------------------------
345
+ def handle_save(st_val, ac_val, sp_val, cur_idx, skip_val):
346
+ skip_flag = (skip_val == "True")
347
+ image_path, sub, obj, _ = get_item_info(cur_idx)
348
+ if skip_flag:
349
+ labeled_data[str(cur_idx)] = {
350
+ "subject": sub,
351
+ "object": obj,
352
+ "skip": True
353
+ }
354
+ save_output_dict(output_file, labeled_data)
355
+ return f"Skipped pair: {sub} - {obj}."
356
+ else:
357
+ if not st_val or not ac_val or not sp_val:
358
+ return "Please select all 3 categories or check 'no suitable option'!"
359
+ labeled_data[str(cur_idx)] = {
360
+ "subject": sub,
361
+ "object": obj,
362
+ "skip": False,
363
+ "state": st_val,
364
+ "action": ac_val,
365
+ "spatial": sp_val
366
+ }
367
+ save_output_dict(output_file, labeled_data)
368
+ return f"Saved: {sub}, {obj}, state={st_val}, action={ac_val}, spatial={sp_val}"
369
+
370
+ btn_save.click(
371
+ fn=handle_save,
372
+ inputs=[state_final, action_final, spatial_final, cur_idx_state, skip_final],
373
+ outputs=status_box
374
+ )
375
+
376
+ return demo
377
+
378
+
379
+ if __name__ == '__main__':
380
+ gradio_interface().launch(share=True)