Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files- apps/paper_image_tool.py +114 -16
apps/paper_image_tool.py
CHANGED
|
@@ -230,15 +230,26 @@ class ImageMatrix:
|
|
| 230 |
result[item['type']] = item['full_path']
|
| 231 |
return result
|
| 232 |
|
| 233 |
-
def process_all(self, rules):
|
| 234 |
# rules: dict of {type: rule_config}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
output_zip = tempfile.mktemp(suffix='.zip')
|
| 236 |
output_dir = tempfile.mkdtemp()
|
| 237 |
|
| 238 |
for item in self.file_map:
|
| 239 |
img_type = item['type']
|
| 240 |
-
|
| 241 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
|
| 243 |
try:
|
| 244 |
img = Image.open(item['full_path'])
|
|
@@ -301,6 +312,47 @@ def apply_image_rule(img, rule):
|
|
| 301 |
matrix = ImageMatrix()
|
| 302 |
# Store rules globally for this session (Not multi-user safe, but fits current architecture)
|
| 303 |
global_rules = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 304 |
|
| 305 |
def handle_upload(file, use_llm_chk):
|
| 306 |
if file is None:
|
|
@@ -310,16 +362,17 @@ def handle_upload(file, use_llm_chk):
|
|
| 310 |
types = matrix.types
|
| 311 |
samples = matrix.samples
|
| 312 |
|
| 313 |
-
# Reset rules
|
| 314 |
global_rules.clear()
|
|
|
|
| 315 |
|
| 316 |
summary_text = f"Found {len(samples)} samples and {len(types)} types.\nTypes: {', '.join(types)}"
|
| 317 |
|
| 318 |
-
return df, gr.update(choices=samples, value=samples[0] if samples else None), gr.update(choices=types, value=types[0] if types else None), summary_text
|
| 319 |
|
| 320 |
def save_rule(type_sel, action, p1, p2, p3, p4):
|
| 321 |
if not type_sel:
|
| 322 |
-
return "No type selected."
|
| 323 |
|
| 324 |
params = {}
|
| 325 |
if action == 'Manual Crop':
|
|
@@ -331,7 +384,33 @@ def save_rule(type_sel, action, p1, p2, p3, p4):
|
|
| 331 |
|
| 332 |
rule = {'action': action, 'params': params}
|
| 333 |
global_rules[type_sel] = rule
|
| 334 |
-
return f"Saved rule for {type_sel}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
|
| 336 |
def update_preview(sample_id, type_sel, action, p1, p2, p3, p4):
|
| 337 |
# p1-p4 are generic params mapped based on action
|
|
@@ -359,15 +438,22 @@ def update_preview(sample_id, type_sel, action, p1, p2, p3, p4):
|
|
| 359 |
path = images[type_sel]
|
| 360 |
orig_img = Image.open(path)
|
| 361 |
proc_img = apply_image_rule(orig_img.copy(), rule)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 362 |
results.append((orig_img, f"{type_sel} (Original)"))
|
| 363 |
-
results.append((proc_img,
|
| 364 |
|
| 365 |
return results
|
| 366 |
|
| 367 |
def run_batch_process():
|
| 368 |
if not matrix.file_map:
|
| 369 |
return None
|
| 370 |
-
return matrix.process_all(global_rules)
|
| 371 |
|
| 372 |
def generate_code_prompt(df_json, user_req):
|
| 373 |
# Fallback for complex needs
|
|
@@ -417,19 +503,24 @@ def create_paper_tool():
|
|
| 417 |
|
| 418 |
with gr.Row():
|
| 419 |
preview_btn = gr.Button("Preview Effect")
|
| 420 |
-
save_rule_btn = gr.Button("Save Rule for Type")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 421 |
|
| 422 |
rule_status = gr.Markdown("")
|
| 423 |
|
| 424 |
with gr.Column():
|
| 425 |
preview_gallery = gr.Gallery(label="Preview", columns=2)
|
| 426 |
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
|
|
|
| 430 |
|
| 431 |
# Event Wiring
|
| 432 |
-
analyze_btn.click(handle_upload, inputs=[zip_input, use_llm_chk], outputs=[file_table, sample_selector, type_selector, structure_info])
|
| 433 |
|
| 434 |
preview_btn.click(update_preview,
|
| 435 |
inputs=[sample_selector, type_selector, action_selector, p1, p2, p3, p4],
|
|
@@ -437,10 +528,17 @@ def create_paper_tool():
|
|
| 437 |
|
| 438 |
save_rule_btn.click(save_rule,
|
| 439 |
inputs=[type_selector, action_selector, p1, p2, p3, p4],
|
| 440 |
-
outputs=[rule_status])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 441 |
|
| 442 |
process_btn.click(run_batch_process, inputs=[], outputs=[download_output])
|
| 443 |
-
|
| 444 |
with gr.Accordion("💻 Code Gen Helper (Fallback)", open=False):
|
| 445 |
user_req = gr.Textbox(label="Describe your requirement")
|
| 446 |
gen_prompt_btn = gr.Button("Generate Prompt")
|
|
|
|
| 230 |
result[item['type']] = item['full_path']
|
| 231 |
return result
|
| 232 |
|
| 233 |
+
def process_all(self, rules, overrides=None):
|
| 234 |
# rules: dict of {type: rule_config}
|
| 235 |
+
# overrides: dict of {sample_id: {type: rule_config}}
|
| 236 |
+
if overrides is None:
|
| 237 |
+
overrides = {}
|
| 238 |
+
|
| 239 |
output_zip = tempfile.mktemp(suffix='.zip')
|
| 240 |
output_dir = tempfile.mkdtemp()
|
| 241 |
|
| 242 |
for item in self.file_map:
|
| 243 |
img_type = item['type']
|
| 244 |
+
sample_id = item['sample_id']
|
| 245 |
+
|
| 246 |
+
# Determine rule: Override > Type Rule > Default
|
| 247 |
+
rule = {'action': 'None'}
|
| 248 |
+
|
| 249 |
+
if sample_id in overrides and img_type in overrides[sample_id]:
|
| 250 |
+
rule = overrides[sample_id][img_type]
|
| 251 |
+
elif img_type in rules:
|
| 252 |
+
rule = rules[img_type]
|
| 253 |
|
| 254 |
try:
|
| 255 |
img = Image.open(item['full_path'])
|
|
|
|
| 312 |
matrix = ImageMatrix()
|
| 313 |
# Store rules globally for this session (Not multi-user safe, but fits current architecture)
|
| 314 |
global_rules = {}
|
| 315 |
+
# Store overrides: {sample_id: {type: rule}}
|
| 316 |
+
global_overrides = {}
|
| 317 |
+
|
| 318 |
+
def get_rules_summary():
|
| 319 |
+
lines = []
|
| 320 |
+
|
| 321 |
+
# Batch Rules
|
| 322 |
+
if global_rules:
|
| 323 |
+
lines.append("#### 📦 Batch Rules")
|
| 324 |
+
for t, r in global_rules.items():
|
| 325 |
+
p = r['params']
|
| 326 |
+
p_str = ""
|
| 327 |
+
if r['action'] == 'Resize':
|
| 328 |
+
p_str = f"{p.get('w')}x{p.get('h')}"
|
| 329 |
+
elif r['action'] == 'Auto Trim':
|
| 330 |
+
p_str = f"Thresh={p.get('threshold')}"
|
| 331 |
+
elif r['action'] == 'Manual Crop':
|
| 332 |
+
p_str = f"x={p.get('x')},y={p.get('y')},w={p.get('w')},h={p.get('h')}"
|
| 333 |
+
|
| 334 |
+
lines.append(f"* **{t}**: {r['action']} `[{p_str}]`")
|
| 335 |
+
|
| 336 |
+
# Overrides
|
| 337 |
+
if global_overrides:
|
| 338 |
+
lines.append("\n#### 🔧 Overrides")
|
| 339 |
+
for sid, types in global_overrides.items():
|
| 340 |
+
for t, r in types.items():
|
| 341 |
+
p = r['params']
|
| 342 |
+
p_str = ""
|
| 343 |
+
if r['action'] == 'Resize':
|
| 344 |
+
p_str = f"{p.get('w')}x{p.get('h')}"
|
| 345 |
+
elif r['action'] == 'Auto Trim':
|
| 346 |
+
p_str = f"Thresh={p.get('threshold')}"
|
| 347 |
+
elif r['action'] == 'Manual Crop':
|
| 348 |
+
p_str = f"x={p.get('x')},y={p.get('y')},w={p.get('w')},h={p.get('h')}"
|
| 349 |
+
|
| 350 |
+
lines.append(f"* **{sid}** ({t}): {r['action']} `[{p_str}]`")
|
| 351 |
+
|
| 352 |
+
if not lines:
|
| 353 |
+
return "No rules configured yet."
|
| 354 |
+
|
| 355 |
+
return "\n".join(lines)
|
| 356 |
|
| 357 |
def handle_upload(file, use_llm_chk):
|
| 358 |
if file is None:
|
|
|
|
| 362 |
types = matrix.types
|
| 363 |
samples = matrix.samples
|
| 364 |
|
| 365 |
+
# Reset rules and overrides
|
| 366 |
global_rules.clear()
|
| 367 |
+
global_overrides.clear()
|
| 368 |
|
| 369 |
summary_text = f"Found {len(samples)} samples and {len(types)} types.\nTypes: {', '.join(types)}"
|
| 370 |
|
| 371 |
+
return df, gr.update(choices=samples, value=samples[0] if samples else None), gr.update(choices=types, value=types[0] if types else None), summary_text, get_rules_summary()
|
| 372 |
|
| 373 |
def save_rule(type_sel, action, p1, p2, p3, p4):
|
| 374 |
if not type_sel:
|
| 375 |
+
return "No type selected.", get_rules_summary()
|
| 376 |
|
| 377 |
params = {}
|
| 378 |
if action == 'Manual Crop':
|
|
|
|
| 384 |
|
| 385 |
rule = {'action': action, 'params': params}
|
| 386 |
global_rules[type_sel] = rule
|
| 387 |
+
return f"Saved rule for Type: {type_sel} -> {action}", get_rules_summary()
|
| 388 |
+
|
| 389 |
+
def save_override(sample_id, type_sel, action, p1, p2, p3, p4):
|
| 390 |
+
if not sample_id or not type_sel:
|
| 391 |
+
return "Sample or Type not selected.", get_rules_summary()
|
| 392 |
+
|
| 393 |
+
params = {}
|
| 394 |
+
if action == 'Manual Crop':
|
| 395 |
+
params = {'x': p1, 'y': p2, 'w': p3, 'h': p4}
|
| 396 |
+
elif action == 'Resize':
|
| 397 |
+
params = {'w': p1, 'h': p2}
|
| 398 |
+
elif action == 'Auto Trim':
|
| 399 |
+
params = {'threshold': p1}
|
| 400 |
+
|
| 401 |
+
rule = {'action': action, 'params': params}
|
| 402 |
+
|
| 403 |
+
if sample_id not in global_overrides:
|
| 404 |
+
global_overrides[sample_id] = {}
|
| 405 |
+
global_overrides[sample_id][type_sel] = rule
|
| 406 |
+
|
| 407 |
+
return f"Saved OVERRIDE for {sample_id} / {type_sel} -> {action}", get_rules_summary()
|
| 408 |
+
|
| 409 |
+
def clear_override(sample_id, type_sel):
|
| 410 |
+
if sample_id in global_overrides and type_sel in global_overrides[sample_id]:
|
| 411 |
+
del global_overrides[sample_id][type_sel]
|
| 412 |
+
return f"Cleared override for {sample_id} / {type_sel}", get_rules_summary()
|
| 413 |
+
return "No override found to clear.", get_rules_summary()
|
| 414 |
|
| 415 |
def update_preview(sample_id, type_sel, action, p1, p2, p3, p4):
|
| 416 |
# p1-p4 are generic params mapped based on action
|
|
|
|
| 438 |
path = images[type_sel]
|
| 439 |
orig_img = Image.open(path)
|
| 440 |
proc_img = apply_image_rule(orig_img.copy(), rule)
|
| 441 |
+
|
| 442 |
+
# Check if override exists
|
| 443 |
+
is_overridden = sample_id in global_overrides and type_sel in global_overrides[sample_id]
|
| 444 |
+
status_text = f"{type_sel} (Processed)"
|
| 445 |
+
if is_overridden:
|
| 446 |
+
status_text += " [OVERRIDE ACTIVE]"
|
| 447 |
+
|
| 448 |
results.append((orig_img, f"{type_sel} (Original)"))
|
| 449 |
+
results.append((proc_img, status_text))
|
| 450 |
|
| 451 |
return results
|
| 452 |
|
| 453 |
def run_batch_process():
|
| 454 |
if not matrix.file_map:
|
| 455 |
return None
|
| 456 |
+
return matrix.process_all(global_rules, global_overrides)
|
| 457 |
|
| 458 |
def generate_code_prompt(df_json, user_req):
|
| 459 |
# Fallback for complex needs
|
|
|
|
| 503 |
|
| 504 |
with gr.Row():
|
| 505 |
preview_btn = gr.Button("Preview Effect")
|
| 506 |
+
save_rule_btn = gr.Button("Save Rule for Type (Batch)")
|
| 507 |
+
|
| 508 |
+
with gr.Row():
|
| 509 |
+
save_override_btn = gr.Button("Save Override (This Sample Only)", variant="secondary")
|
| 510 |
+
clear_override_btn = gr.Button("Clear Override", variant="stop")
|
| 511 |
|
| 512 |
rule_status = gr.Markdown("")
|
| 513 |
|
| 514 |
with gr.Column():
|
| 515 |
preview_gallery = gr.Gallery(label="Preview", columns=2)
|
| 516 |
|
| 517 |
+
gr.Markdown("### 🚀 Batch Process")
|
| 518 |
+
rules_summary_display = gr.Markdown("No rules configured yet.")
|
| 519 |
+
process_btn = gr.Button("Apply Rules & Download", variant="primary")
|
| 520 |
+
download_output = gr.File(label="Download Result")
|
| 521 |
|
| 522 |
# Event Wiring
|
| 523 |
+
analyze_btn.click(handle_upload, inputs=[zip_input, use_llm_chk], outputs=[file_table, sample_selector, type_selector, structure_info, rules_summary_display])
|
| 524 |
|
| 525 |
preview_btn.click(update_preview,
|
| 526 |
inputs=[sample_selector, type_selector, action_selector, p1, p2, p3, p4],
|
|
|
|
| 528 |
|
| 529 |
save_rule_btn.click(save_rule,
|
| 530 |
inputs=[type_selector, action_selector, p1, p2, p3, p4],
|
| 531 |
+
outputs=[rule_status, rules_summary_display])
|
| 532 |
+
|
| 533 |
+
save_override_btn.click(save_override,
|
| 534 |
+
inputs=[sample_selector, type_selector, action_selector, p1, p2, p3, p4],
|
| 535 |
+
outputs=[rule_status, rules_summary_display])
|
| 536 |
+
|
| 537 |
+
clear_override_btn.click(clear_override,
|
| 538 |
+
inputs=[sample_selector, type_selector],
|
| 539 |
+
outputs=[rule_status, rules_summary_display])
|
| 540 |
|
| 541 |
process_btn.click(run_batch_process, inputs=[], outputs=[download_output])
|
|
|
|
| 542 |
with gr.Accordion("💻 Code Gen Helper (Fallback)", open=False):
|
| 543 |
user_req = gr.Textbox(label="Describe your requirement")
|
| 544 |
gen_prompt_btn = gr.Button("Generate Prompt")
|