Emilyxml commited on
Commit
32d8a37
·
verified ·
1 Parent(s): 82a791c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +44 -244
app.py CHANGED
@@ -1,252 +1,52 @@
1
- import gradio as gr
2
- import os
3
- import random
4
- import uuid
5
- import csv
6
- from datetime import datetime
7
- from pathlib import Path
8
- from PIL import Image
9
- from huggingface_hub import CommitScheduler, snapshot_download
10
-
11
- # --- 1. 配置区域 ---
12
- DATASET_REPO_ID = "Emilyxml/moveit"
13
- DATA_FOLDER = "data"
14
- LOG_FOLDER = Path("logs")
15
- LOG_FOLDER.mkdir(parents=True, exist_ok=True)
16
- TOKEN = os.environ.get("HF_TOKEN")
17
-
18
- # --- 2. 自动下载数据 ---
19
- # 只有本地为空时才下载,避免每次重启都浪费时间
20
- if not os.path.exists(DATA_FOLDER) or not os.listdir(DATA_FOLDER):
21
- try:
22
- print("🚀 正在从 Dataset 下载数据...")
23
- snapshot_download(
24
- repo_id=DATASET_REPO_ID,
25
- repo_type="dataset",
26
- local_dir=DATA_FOLDER,
27
- token=TOKEN,
28
- allow_patterns=["*.jpg", "*.png", "*.jpeg", "*.webp", "*.txt"]
29
  )
30
- print("✅ 数据下载完成!")
31
- except Exception as e:
32
- print(f"⚠️ 下载失败: {e}")
33
-
34
- # --- 3. 恢复后台同步 (解决点击卡顿的关键) ---
35
- # 使用 Scheduler,提交操作不需要等待网络上传,速度最快
36
- scheduler = CommitScheduler(
37
- repo_id=DATASET_REPO_ID,
38
- repo_type="dataset",
39
- folder_path=LOG_FOLDER,
40
- path_in_repo="logs",
41
- every=1, # 每1分钟同步一次
42
- token=TOKEN
43
- )
44
-
45
- # --- 4. 数据加载 ---
46
- def load_data():
47
- groups = {}
48
- if not os.path.exists(DATA_FOLDER):
49
- return {}, []
50
-
51
- for filename in os.listdir(DATA_FOLDER):
52
- if filename.startswith('.'): continue
53
- file_path = os.path.join(DATA_FOLDER, filename)
54
- prefix = filename[:5]
55
 
56
- if prefix not in groups:
57
- groups[prefix] = {"origin": None, "candidates": [], "instruction": "暂无说明"}
58
-
59
- if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.webp')):
60
- if "_origin" in filename.lower():
61
- groups[prefix]["origin"] = file_path
62
- else:
63
- groups[prefix]["candidates"].append(file_path)
64
- elif filename.lower().endswith('.txt'):
65
- try:
66
- with open(file_path, "r", encoding="utf-8") as f:
67
- groups[prefix]["instruction"] = f.read()
68
- except:
69
- with open(file_path, "r", encoding="gbk") as f:
70
- groups[prefix]["instruction"] = f.read()
71
-
72
- valid_groups = {}
73
- for k, v in groups.items():
74
- if v["origin"] is not None or len(v["candidates"]) > 0:
75
- valid_groups[k] = v
76
-
77
- group_ids = list(valid_groups.keys())
78
- random.shuffle(group_ids)
79
- print(f"Loaded {len(group_ids)} groups.")
80
- return valid_groups, group_ids
81
-
82
- ALL_GROUPS, ALL_GROUP_IDS = load_data()
83
-
84
- # --- NEW: 更激进的图片优化 (解决加载慢的关键) ---
85
- def optimize_image(image_path, max_width=500):
86
- """
87
- 调整大小至 500px,对于 User Study 的缩略图查看完全足够。
88
- """
89
- if not image_path:
90
- return None
91
- try:
92
- img = Image.open(image_path)
93
- # 转换为 RGB 防止 PNG 透明通道在 JPEG 转换时报错
94
- if img.mode in ("RGBA", "P"):
95
- img = img.convert("RGB")
96
-
97
- if img.width > max_width:
98
- ratio = max_width / img.width
99
- new_height = int(img.height * ratio)
100
- # 使用 LANCZOS 算法保证缩放质量
101
- img = img.resize((max_width, new_height), Image.LANCZOS)
102
- return img
103
- except Exception as e:
104
- print(f"Error loading image {image_path}: {e}")
105
- return None
106
-
107
- # --- 5. 核心逻辑 ---
108
-
109
- def get_next_question(user_state):
110
- idx = user_state["index"]
111
-
112
- # 结束逻辑
113
- if idx >= len(ALL_GROUP_IDS):
114
- return (
115
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
116
- gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
117
- gr.update(value="## 🎉 测试结束!感谢您的参与。", visible=True),
118
- user_state, []
119
  )
120
-
121
- group_id = ALL_GROUP_IDS[idx]
122
- group_data = ALL_GROUPS[group_id]
123
-
124
- # 1. 优化原图
125
- origin_img = optimize_image(group_data["origin"], max_width=500)
126
-
127
- # 2. 优化候选图
128
- candidates = group_data["candidates"].copy()
129
- random.shuffle(candidates)
130
-
131
- gallery_items = []
132
- choices = []
133
- candidates_info = []
134
-
135
- for i, path in enumerate(candidates):
136
- label = f"Option {chr(65+i)}"
137
- # 优化每张候选图
138
- optimized_img = optimize_image(path, max_width=500)
139
- gallery_items.append((optimized_img, label))
140
- choices.append(label)
141
- candidates_info.append({"label": label, "path": path})
142
 
143
- instruction = f"### 任务 ({idx + 1} / {len(ALL_GROUP_IDS)})\n\n{group_data['instruction']}"
144
-
145
- return (
146
- gr.update(value=origin_img, visible=True if origin_img else False),
147
- gr.update(value=gallery_items, visible=True),
148
- gr.update(choices=choices, value=[], visible=True),
149
- gr.update(value=instruction, visible=True),
150
- gr.update(visible=True), gr.update(visible=True), gr.update(visible=False),
151
- user_state, candidates_info
152
- )
153
-
154
- def save_and_next(user_state, candidates_info, selected_options, is_none=False):
155
- current_idx = user_state["index"]
156
- group_id = ALL_GROUP_IDS[current_idx]
157
-
158
- if is_none:
159
- choice_str = "Rejected All"
160
- method_str = "None_Satisfied"
161
- else:
162
- if not selected_options:
163
- raise gr.Error("请至少勾选一个选项,或点击“都不满意”")
164
- choice_str = "; ".join(selected_options)
165
- selected_methods = []
166
- for opt in selected_options:
167
- for info in candidates_info:
168
- if info["label"] == opt:
169
- path = info["path"]
170
- filename = os.path.basename(path)
171
- name = os.path.splitext(filename)[0]
172
- parts = name.split('_', 1)
173
- method = parts[1] if len(parts) > 1 else name
174
- selected_methods.append(method)
175
- break
176
- method_str = "; ".join(selected_methods)
177
 
178
- # --- 极速保存:只写本地文件,不等待网络上传 ---
179
- user_filename = f"user_{user_state['user_id']}.csv"
180
- user_file_path = LOG_FOLDER / user_filename
181
-
182
- # 使用 Scheduler 提供的锁来保证多线程安全
183
- with scheduler.lock:
184
- file_exists = user_file_path.exists()
185
- with open(user_file_path, "a", newline="", encoding="utf-8") as f:
186
- writer = csv.writer(f)
187
- if not file_exists:
188
- writer.writerow(["user_id", "timestamp", "group_id", "choices", "methods"])
189
- writer.writerow([
190
- user_state["user_id"],
191
- datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
192
- group_id,
193
- choice_str,
194
- method_str
195
- ])
196
-
197
- print(f"✅ Local Saved: {group_id} (Upload will happen in background)")
198
-
199
- user_state["index"] += 1
200
- return get_next_question(user_state)
201
 
202
- # --- 6. 界面构建 ---
203
- with gr.Blocks(title="User Study") as demo:
204
-
205
- state_user = gr.State(lambda: {"user_id": str(uuid.uuid4())[:8], "index": 0})
206
- state_candidates_info = gr.State([])
207
-
208
- with gr.Row():
209
- md_instruction = gr.Markdown("Loading...")
210
-
211
- with gr.Row():
212
- with gr.Column(scale=1):
213
- # 强制 JPEG 格式,quality 默认 90,显示速度快
214
- img_origin = gr.Image(label="Reference (参考原图)", interactive=False, height=400, format="jpeg")
215
-
216
- with gr.Column(scale=2):
217
- gallery_candidates = gr.Gallery(
218
- label="Candidates (候选结果)",
219
- columns=[2], height="auto", object_fit="contain", interactive=False, format="jpeg"
220
- )
221
- gr.Markdown("👇 **请在下方勾选您认为最好的结果(可多选):**")
222
- checkbox_options = gr.CheckboxGroup(choices=[], label="您的选择", info="对应上方图片的标签")
223
-
224
- with gr.Row():
225
- btn_submit = gr.Button("🚀 提交 (Submit)", variant="primary")
226
- btn_none = gr.Button("🚫 都不满意 (None)", variant="stop")
227
-
228
- md_end = gr.Markdown(visible=False)
229
 
230
- # 初始加载
231
- demo.load(
232
- fn=get_next_question,
233
- inputs=[state_user],
234
- outputs=[img_origin, gallery_candidates, checkbox_options, md_instruction, btn_submit, btn_none, md_end, state_user, state_candidates_info]
235
- )
236
-
237
- # 提交按钮事件
238
- btn_submit.click(
239
- fn=lambda s, c, o: save_and_next(s, c, o, is_none=False),
240
- inputs=[state_user, state_candidates_info, checkbox_options],
241
- outputs=[img_origin, gallery_candidates, checkbox_options, md_instruction, btn_submit, btn_none, md_end, state_user, state_candidates_info]
242
- )
243
-
244
- # 都不满意按钮事件
245
- btn_none.click(
246
- fn=lambda s, c, o: save_and_next(s, c, o, is_none=True),
247
- inputs=[state_user, state_candidates_info, checkbox_options],
248
- outputs=[img_origin, gallery_candidates, checkbox_options, md_instruction, btn_submit, btn_none, md_end, state_user, state_candidates_info]
249
- )
250
 
251
- if __name__ == "__main__":
252
- demo.launch()
 
 
 
 
1
+ state_user = gr.State(lambda: {"user_id": str(uuid.uuid4())[:8], "index": 0})
2
+ state_candidates_info = gr.State([])
3
+
4
+ with gr.Row():
5
+ md_instruction = gr.Markdown("Loading...")
6
+
7
+ with gr.Row():
8
+ with gr.Column(scale=1):
9
+ # format 设置为 jpeg 进一步减小体积
10
+ img_origin = gr.Image(label="Reference (参考原图)", interactive=False, height=400, format="jpeg")
11
+
12
+ with gr.Column(scale=2):
13
+ gallery_candidates = gr.Gallery(
14
+ label="Candidates (候选结果)",
15
+ columns=[2],
16
+ height="auto",
17
+ object_fit="contain",
18
+ interactive=False,
19
+ format="jpeg" # 强制输出 JPEG 格式
 
 
 
 
 
 
 
 
 
20
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
+ gr.Markdown("👇 **请在下方勾选您认为最好的结果(可多选):**")
23
+
24
+ checkbox_options = gr.CheckboxGroup(
25
+ choices=[],
26
+ label="您的选择",
27
+ info="对应上方图片的标签 (Option A, B...)"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
+ with gr.Row():
31
+ btn_submit = gr.Button("🚀 提交 (Submit)", variant="primary")
32
+ btn_none = gr.Button("🚫 都不满意 (None)", variant="stop")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
+ md_end = gr.Markdown(visible=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
+ demo.load(
37
+ fn=get_next_question,
38
+ inputs=[state_user],
39
+ outputs=[img_origin, gallery_candidates, checkbox_options, md_instruction, btn_submit, btn_none, md_end, state_user, state_candidates_info]
40
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
+ btn_submit.click(
43
+ fn=lambda s, c, o: save_and_next(s, c, o, is_none=False),
44
+ inputs=[state_user, state_candidates_info, checkbox_options],
45
+ outputs=[img_origin, gallery_candidates, checkbox_options, md_instruction, btn_submit, btn_none, md_end, state_user, state_candidates_info]
46
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
+ btn_none.click(
49
+ fn=lambda s, c, o: save_and_next(s, c, o, is_none=True),
50
+ inputs=[state_user, state_candidates_info, checkbox_options],
51
+ outputs=[img_origin, gallery_candidates, checkbox_options, md_instruction, btn_submit, btn_none, md_end, state_user, state_candidates_info]
52
+ )