JS6969 commited on
Commit
305fb2f
·
verified ·
1 Parent(s): 5cd2a27

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -37
app.py CHANGED
@@ -1,16 +1,6 @@
1
  # =====================================================================
2
  # ForgeCaptions - Gradio app for single & batch image captioning
3
  # =====================================================================
4
- # CHANGELOG (this version)
5
- # - GPU-safe: all CUDA only inside @spaces.GPU functions.
6
- # - Restored: Single tab + Batch chunking (Auto / All-at-once / Manual step).
7
- # - Shape Aliases: supports comma/pipe-separated synonyms per row.
8
- # - Default caption style: "Character training (long)".
9
- # - Model Instructions + Caption Style in minimizable accordions.
10
- # - Excel export: thumbnail size slider controls image scaling & row height.
11
- # - Header logo scaled to the full text stack (centered).
12
- # - Kept gallery & table positions unchanged; scroll sync retained.
13
- # =====================================================================
14
 
15
  # ------------------------------
16
  # 0) Imports & environment
@@ -408,22 +398,30 @@ def run_batch(
408
  top_p: float,
409
  max_tokens: int,
410
  max_side: int,
411
- ) -> Tuple[List[dict], list, list, str]:
412
- """
413
- Process a list of file paths and append results to session_rows.
414
- Returns: updated rows, gallery_pairs, table_rows, status_text
 
 
 
415
  """
416
  session_rows = session_rows or []
417
- files = files or []
418
- if not files:
 
 
 
419
  gallery_pairs = [((r.get("thumb_path") or r.get("path")), r.get("caption",""))
420
  for r in session_rows if (r.get("thumb_path") or r.get("path"))]
421
  table_rows = [[r.get("filename",""), r.get("caption","")] for r in session_rows]
422
- return session_rows, gallery_pairs, table_rows, f"Saved • {time.strftime('%H:%M:%S')}"
423
 
424
- for path in files:
425
- if not path or not os.path.exists(path):
426
- continue
 
 
427
  try:
428
  im = Image.open(path).convert("RGB")
429
  except Exception:
@@ -436,12 +434,26 @@ def run_batch(
436
  filename = os.path.basename(path)
437
  thumb = ensure_thumb(path, 256)
438
  session_rows.append({"filename": filename, "caption": cap, "path": path, "thumb_path": thumb})
 
 
 
 
 
 
439
 
440
  save_session(session_rows)
441
  gallery_pairs = [((r.get("thumb_path") or r.get("path")), r.get("caption",""))
442
  for r in session_rows if (r.get("thumb_path") or r.get("path"))]
443
  table_rows = [[r.get("filename",""), r.get("caption","")] for r in session_rows]
444
- return session_rows, gallery_pairs, table_rows, f"Saved • {time.strftime('%H:%M:%S')}"
 
 
 
 
 
 
 
 
445
 
446
  # Ensure Spaces detects at least one GPU function at startup
447
  @gpu
@@ -632,6 +644,7 @@ setTimeout(() => {{
632
  label="Batch mode"
633
  )
634
  chunk_size = gr.Slider(1, 50, value=10, step=1, label="Chunk size")
 
635
 
636
  # -- Keep instruction text in sync with controls and persist to settings
637
  def _refresh_instruction(styles, extra, name_value, trigv, begv, endv, excel_px, ms):
@@ -676,6 +689,7 @@ setTimeout(() => {{
676
  # ---- Results (UNCHANGED POSITION): Gallery left, Table right
677
  rows_state = gr.State(load_session())
678
  autosave_md = gr.Markdown("Ready.")
 
679
  remaining_state = gr.State([]) # for manual step mode
680
 
681
  with gr.Row():
@@ -770,45 +784,66 @@ setTimeout(() => {{
770
  s = load_settings()
771
  return s.get("temperature", 0.6), s.get("top_p", 0.9), s.get("max_tokens", 256)
772
 
773
- def _run_click(files, rows, instr, ms, mode, csize):
774
  t, p, m = _tpms()
775
  files = files or []
 
776
  # Manual step → process first chunk only
777
  if mode == "Manual (step)" and files:
778
  chunks = _split_chunks(files, int(csize))
779
  batch = chunks[0]
780
  remaining = sum(chunks[1:], [])
781
- new_rows, gal, tbl, stamp = run_batch(batch, rows or [], instr, t, p, m, int(ms))
 
 
 
782
  panel_vis = gr.update(visible=bool(remaining))
783
  msg = f"{len(remaining)} files remain. Process next chunk?"
784
- return new_rows, gal, tbl, stamp, remaining, panel_vis, gr.update(value=msg)
785
- # Auto / all-at-once process everything in one go
786
- else:
787
- new_rows, gal, tbl, stamp = run_batch(files, rows or [], instr, t, p, m, int(ms))
788
- return new_rows, gal, tbl, stamp, [], gr.update(visible=False), gr.update(value="")
789
 
 
 
 
 
 
 
 
 
 
 
790
  run_button.click(
791
  _run_click,
792
- inputs=[input_files, rows_state, instruction_preview, max_side, chunk_mode, chunk_size],
793
- outputs=[rows_state, gallery, table, autosave_md, remaining_state, step_panel, step_msg]
794
  )
795
 
796
- def _step_next(remain, rows, instr, ms, csize):
797
  t, p, m = _tpms()
798
  remain = remain or []
799
  if not remain:
800
- return rows, gr.update(value="No files remaining."), gr.update(visible=False), [], [], [], "Saved."
 
801
  batch = remain[:int(csize)]
802
  leftover = remain[int(csize):]
803
- new_rows, gal, tbl, stamp = run_batch(batch, rows or [], instr, t, p, m, int(ms))
 
 
 
804
  panel_vis = gr.update(visible=bool(leftover))
805
  msg = f"{len(leftover)} files remain. Process next chunk?" if leftover else "All done."
806
- return new_rows, msg, panel_vis, leftover, gal, tbl, stamp
807
-
 
 
808
  step_next.click(
809
  _step_next,
810
- inputs=[remaining_state, rows_state, instruction_preview, max_side, chunk_size],
811
- outputs=[rows_state, step_msg, step_panel, remaining_state, gallery, table, autosave_md]
 
 
 
 
812
  )
813
 
814
  def _step_finish():
 
1
  # =====================================================================
2
  # ForgeCaptions - Gradio app for single & batch image captioning
3
  # =====================================================================
 
 
 
 
 
 
 
 
 
 
4
 
5
  # ------------------------------
6
  # 0) Imports & environment
 
398
  top_p: float,
399
  max_tokens: int,
400
  max_side: int,
401
+ time_budget_s: float | None = None, # respects Zero-GPU window
402
+ progress: gr.Progress = gr.Progress(track_tqdm=True), # drives the progress bar
403
+ ) -> Tuple[List[dict], list, list, str, List[str], int, int]:
404
+ """
405
+ Returns:
406
+ session_rows, gallery_pairs, table_rows, status_text,
407
+ leftover_files, processed_in_this_call, total_in_this_call
408
  """
409
  session_rows = session_rows or []
410
+ files = [f for f in (files or []) if f and os.path.exists(f)]
411
+ total = len(files)
412
+ processed = 0
413
+
414
+ if total == 0:
415
  gallery_pairs = [((r.get("thumb_path") or r.get("path")), r.get("caption",""))
416
  for r in session_rows if (r.get("thumb_path") or r.get("path"))]
417
  table_rows = [[r.get("filename",""), r.get("caption","")] for r in session_rows]
418
+ return session_rows, gallery_pairs, table_rows, f"Saved • {time.strftime('%H:%M:%S')}", [], 0, 0
419
 
420
+ start = time.time()
421
+ leftover: List[str] = []
422
+
423
+ # Progress bar shows inside the GPU worker
424
+ for idx, path in enumerate(progress.tqdm(files, desc="Captioning")):
425
  try:
426
  im = Image.open(path).convert("RGB")
427
  except Exception:
 
434
  filename = os.path.basename(path)
435
  thumb = ensure_thumb(path, 256)
436
  session_rows.append({"filename": filename, "caption": cap, "path": path, "thumb_path": thumb})
437
+ processed += 1
438
+
439
+ # Time-slice to avoid Zero GPU timeouts
440
+ if time_budget_s and (time.time() - start) >= float(time_budget_s):
441
+ leftover = files[idx+1:]
442
+ break
443
 
444
  save_session(session_rows)
445
  gallery_pairs = [((r.get("thumb_path") or r.get("path")), r.get("caption",""))
446
  for r in session_rows if (r.get("thumb_path") or r.get("path"))]
447
  table_rows = [[r.get("filename",""), r.get("caption","")] for r in session_rows]
448
+ return (
449
+ session_rows,
450
+ gallery_pairs,
451
+ table_rows,
452
+ f"Saved • {time.strftime('%H:%M:%S')}",
453
+ leftover,
454
+ processed,
455
+ total,
456
+ )
457
 
458
  # Ensure Spaces detects at least one GPU function at startup
459
  @gpu
 
644
  label="Batch mode"
645
  )
646
  chunk_size = gr.Slider(1, 50, value=10, step=1, label="Chunk size")
647
+ gpu_budget = gr.Slider(20, 110, value=55, step=5, label="Max seconds per GPU call")
648
 
649
  # -- Keep instruction text in sync with controls and persist to settings
650
  def _refresh_instruction(styles, extra, name_value, trigv, begv, endv, excel_px, ms):
 
689
  # ---- Results (UNCHANGED POSITION): Gallery left, Table right
690
  rows_state = gr.State(load_session())
691
  autosave_md = gr.Markdown("Ready.")
692
+ progress_md = gr.Markdown("")
693
  remaining_state = gr.State([]) # for manual step mode
694
 
695
  with gr.Row():
 
784
  s = load_settings()
785
  return s.get("temperature", 0.6), s.get("top_p", 0.9), s.get("max_tokens", 256)
786
 
787
+ def _run_click(files, rows, instr, ms, mode, csize, budget_s):
788
  t, p, m = _tpms()
789
  files = files or []
790
+
791
  # Manual step → process first chunk only
792
  if mode == "Manual (step)" and files:
793
  chunks = _split_chunks(files, int(csize))
794
  batch = chunks[0]
795
  remaining = sum(chunks[1:], [])
796
+ new_rows, gal, tbl, stamp, leftover_from_batch, done, total = run_batch(
797
+ batch, rows or [], instr, t, p, m, int(ms), float(budget_s)
798
+ )
799
+ remaining = (leftover_from_batch or []) + remaining
800
  panel_vis = gr.update(visible=bool(remaining))
801
  msg = f"{len(remaining)} files remain. Process next chunk?"
802
+ prog = f"Batch progress: {done}/{total} processed in this step • Remaining overall: {len(remaining)}"
803
+ return new_rows, gal, tbl, stamp, remaining, panel_vis, gr.update(value=msg), gr.update(value=prog)
 
 
 
804
 
805
+ # Auto / All-at-once (still obey time budget)
806
+ new_rows, gal, tbl, stamp, leftover, done, total = run_batch(
807
+ files, rows or [], instr, t, p, m, int(ms), float(budget_s)
808
+ )
809
+ panel_vis = gr.update(visible=bool(leftover))
810
+ msg = f"{len(leftover)} files remain. Process next chunk?" if leftover else ""
811
+ prog = f"Batch progress: {done}/{total} processed in this call • Remaining: {len(leftover)}"
812
+ return new_rows, gal, tbl, stamp, leftover, panel_vis, gr.update(value=msg), gr.update(value=prog)
813
+
814
+
815
  run_button.click(
816
  _run_click,
817
+ inputs=[input_files, rows_state, instruction_preview, max_side, chunk_mode, chunk_size, gpu_budget],
818
+ outputs=[rows_state, gallery, table, autosave_md, remaining_state, step_panel, step_msg, progress_md]
819
  )
820
 
821
+ def _step_next(remain, rows, instr, ms, csize, budget_s):
822
  t, p, m = _tpms()
823
  remain = remain or []
824
  if not remain:
825
+ return rows, gr.update(value="No files remaining."), gr.update(visible=False), [], [], [], "Saved.", gr.update(value="")
826
+
827
  batch = remain[:int(csize)]
828
  leftover = remain[int(csize):]
829
+ new_rows, gal, tbl, stamp, leftover_from_batch, done, total = run_batch(
830
+ batch, rows or [], instr, t, p, m, int(ms), float(budget_s)
831
+ )
832
+ leftover = (leftover_from_batch or []) + leftover
833
  panel_vis = gr.update(visible=bool(leftover))
834
  msg = f"{len(leftover)} files remain. Process next chunk?" if leftover else "All done."
835
+ prog = f"Batch progress: {done}/{total} processed in this step • Remaining overall: {len(leftover)}"
836
+ return new_rows, msg, panel_vis, leftover, gal, tbl, stamp, gr.update(value=prog)
837
+
838
+ # hookup (with gpu_budget slider)
839
  step_next.click(
840
  _step_next,
841
+ inputs=[remaining_state, rows_state, instruction_preview, max_side, chunk_size, gpu_budget],
842
+ outputs=[rows_state, step_msg, step_panel, remaining_state, gallery, table, autosave_md, progress_md]
843
+ )
844
+
845
+ # if you don't have gpu_budget: wrap with a lambda and pass 55 as above
846
+
847
  )
848
 
849
  def _step_finish():