muk42 commited on
Commit
ed2bf63
·
1 Parent(s): 7870519

progress tracking, cursor

Browse files
annotation_tab/annotation_logic.py CHANGED
@@ -5,9 +5,17 @@ import gradio as gr
5
  from config import OUTPUT_DIR
6
 
7
  # ==== CONFIG ====
8
- IMAGE_FOLDER = os.path.join(OUTPUT_DIR,"blobs")
9
  os.makedirs(IMAGE_FOLDER, exist_ok=True)
10
- CSV_FILE = os.path.join(OUTPUT_DIR,"annotations.csv")
 
 
 
 
 
 
 
 
11
 
12
  # ==== STATE ====
13
  if os.path.exists(CSV_FILE):
@@ -22,14 +30,17 @@ all_images = [f for f in os.listdir(IMAGE_FOLDER) if f.lower().endswith(('.png',
22
  all_images_paths = [os.path.join(IMAGE_FOLDER, f) for f in all_images]
23
  current_index = 0
24
 
 
25
  def get_current_image_path():
26
  if 0 <= current_index < len(all_images_paths):
27
  return all_images_paths[current_index]
28
  return None
29
 
 
30
  def is_annotated(image_path):
31
  return os.path.basename(image_path) in annotated_ids
32
 
 
33
  def get_annotation_for_image(image_path):
34
  filename = os.path.basename(image_path)
35
  row = df_annotations[df_annotations["blob_id"] == filename]
@@ -37,6 +48,7 @@ def get_annotation_for_image(image_path):
37
  return row["human_ocr"].values[0]
38
  return ""
39
 
 
40
  def find_next_unannotated_index(start):
41
  n = len(all_images_paths)
42
  idx = start
@@ -46,6 +58,7 @@ def find_next_unannotated_index(start):
46
  return idx
47
  return None
48
 
 
49
  def save_annotation(user_text):
50
  global df_annotations, annotated_ids
51
  img_path = get_current_image_path()
@@ -62,30 +75,33 @@ def save_annotation(user_text):
62
 
63
  df_annotations.to_csv(CSV_FILE, index=False)
64
 
 
65
  def save_and_next(user_text):
66
  global current_index
67
  if get_current_image_path() is None:
68
- return None, "", gr.update(visible=True, value="No images available."), "No image loaded"
69
 
70
  save_annotation(user_text)
71
  next_idx = find_next_unannotated_index(current_index)
72
  if next_idx is None:
73
- return None, "", gr.update(visible=True, value="All images annotated."), ""
74
 
75
  current_index = next_idx
76
  img_path = get_current_image_path()
77
  annotation = get_annotation_for_image(img_path)
78
- return img_path, annotation, gr.update(visible=False), img_path
 
79
 
80
  def previous_image():
81
  global current_index
82
  if len(all_images_paths) == 0:
83
- return None, "", gr.update(visible=True, value="No images available."), "No image loaded"
84
 
85
  current_index = (current_index - 1) % len(all_images_paths)
86
  img_path = get_current_image_path()
87
  annotation = get_annotation_for_image(img_path)
88
- return img_path, annotation, gr.update(visible=False), img_path
 
89
 
90
  def delete_and_next():
91
  global current_index, all_images_paths, annotated_ids, df_annotations
@@ -102,27 +118,29 @@ def delete_and_next():
102
  del all_images_paths[current_index]
103
 
104
  if len(all_images_paths) == 0:
105
- return None, "", gr.update(visible=True, value="No images left."), "No image loaded"
106
 
107
  current_index = min(current_index, len(all_images_paths) - 1)
108
  img_path = get_current_image_path()
109
  annotation = get_annotation_for_image(img_path)
110
- return img_path, annotation, gr.update(visible=False), img_path
 
111
 
112
  def shutdown():
113
  os._exit(0)
114
 
 
115
  def save_and_exit(user_text):
116
  if get_current_image_path() is not None:
117
  save_annotation(user_text)
118
  threading.Timer(1, shutdown).start()
119
- return None, "", gr.update(visible=True, value="Session closed."), ""
120
 
121
 
122
  def get_current_annotations_path():
123
- import os
124
  return os.path.join(OUTPUT_DIR, "annotations.csv")
125
 
 
126
  def refresh_image_list():
127
  """
128
  Rescan the blobs folder and update global image list.
@@ -134,8 +152,8 @@ def refresh_image_list():
134
  current_index = 0
135
 
136
  if not all_images_paths:
137
- return None, "", gr.update(visible=True, value="No images available."), "No image loaded"
138
 
139
  img_path = get_current_image_path()
140
  annotation = get_annotation_for_image(img_path)
141
- return img_path, annotation, gr.update(visible=False), img_path
 
5
  from config import OUTPUT_DIR
6
 
7
  # ==== CONFIG ====
8
+ IMAGE_FOLDER = os.path.join(OUTPUT_DIR, "blobs")
9
  os.makedirs(IMAGE_FOLDER, exist_ok=True)
10
+ CSV_FILE = os.path.join(OUTPUT_DIR, "annotations.csv")
11
+
12
+
13
+ # ==== HELPER COUNTER ====
14
+ def get_progress_text():
15
+ if not all_images_paths:
16
+ return "No images loaded"
17
+ return f"Image {current_index + 1} of {len(all_images_paths)}"
18
+
19
 
20
  # ==== STATE ====
21
  if os.path.exists(CSV_FILE):
 
30
  all_images_paths = [os.path.join(IMAGE_FOLDER, f) for f in all_images]
31
  current_index = 0
32
 
33
+
34
  def get_current_image_path():
35
  if 0 <= current_index < len(all_images_paths):
36
  return all_images_paths[current_index]
37
  return None
38
 
39
+
40
  def is_annotated(image_path):
41
  return os.path.basename(image_path) in annotated_ids
42
 
43
+
44
  def get_annotation_for_image(image_path):
45
  filename = os.path.basename(image_path)
46
  row = df_annotations[df_annotations["blob_id"] == filename]
 
48
  return row["human_ocr"].values[0]
49
  return ""
50
 
51
+
52
  def find_next_unannotated_index(start):
53
  n = len(all_images_paths)
54
  idx = start
 
58
  return idx
59
  return None
60
 
61
+
62
  def save_annotation(user_text):
63
  global df_annotations, annotated_ids
64
  img_path = get_current_image_path()
 
75
 
76
  df_annotations.to_csv(CSV_FILE, index=False)
77
 
78
+
79
  def save_and_next(user_text):
80
  global current_index
81
  if get_current_image_path() is None:
82
+ return None, "", gr.update(visible=True, value="No images available."), "No image loaded", "No images loaded"
83
 
84
  save_annotation(user_text)
85
  next_idx = find_next_unannotated_index(current_index)
86
  if next_idx is None:
87
+ return None, "", gr.update(visible=True, value="All images annotated."), "", get_progress_text()
88
 
89
  current_index = next_idx
90
  img_path = get_current_image_path()
91
  annotation = get_annotation_for_image(img_path)
92
+ return img_path, annotation, gr.update(visible=False), img_path, get_progress_text()
93
+
94
 
95
  def previous_image():
96
  global current_index
97
  if len(all_images_paths) == 0:
98
+ return None, "", gr.update(visible=True, value="No images available."), "No image loaded", "No images loaded"
99
 
100
  current_index = (current_index - 1) % len(all_images_paths)
101
  img_path = get_current_image_path()
102
  annotation = get_annotation_for_image(img_path)
103
+ return img_path, annotation, gr.update(visible=False), img_path, get_progress_text()
104
+
105
 
106
  def delete_and_next():
107
  global current_index, all_images_paths, annotated_ids, df_annotations
 
118
  del all_images_paths[current_index]
119
 
120
  if len(all_images_paths) == 0:
121
+ return None, "", gr.update(visible=True, value="No images left."), "No image loaded", "No images loaded"
122
 
123
  current_index = min(current_index, len(all_images_paths) - 1)
124
  img_path = get_current_image_path()
125
  annotation = get_annotation_for_image(img_path)
126
+ return img_path, annotation, gr.update(visible=False), img_path, get_progress_text()
127
+
128
 
129
  def shutdown():
130
  os._exit(0)
131
 
132
+
133
  def save_and_exit(user_text):
134
  if get_current_image_path() is not None:
135
  save_annotation(user_text)
136
  threading.Timer(1, shutdown).start()
137
+ return None, "", gr.update(visible=True, value="Session closed."), "", get_progress_text()
138
 
139
 
140
  def get_current_annotations_path():
 
141
  return os.path.join(OUTPUT_DIR, "annotations.csv")
142
 
143
+
144
  def refresh_image_list():
145
  """
146
  Rescan the blobs folder and update global image list.
 
152
  current_index = 0
153
 
154
  if not all_images_paths:
155
+ return None, "", gr.update(visible=True, value="No images available."), "No image loaded", "No images loaded"
156
 
157
  img_path = get_current_image_path()
158
  annotation = get_annotation_for_image(img_path)
159
+ return img_path, annotation, gr.update(visible=False), img_path, get_progress_text()
annotation_tab/annotation_setup.py CHANGED
@@ -1,39 +1,64 @@
1
  import gradio as gr
2
  from .annotation_logic import (
3
  save_and_next, previous_image, delete_and_next, save_and_exit,
4
- get_current_image_path, get_annotation_for_image, get_current_annotations_path,refresh_image_list
 
5
  )
6
 
7
 
8
-
9
-
10
  def get_annotation_widgets():
11
  message = gr.Markdown("", visible=False)
12
- image_path_display = gr.Markdown(value=get_current_image_path() or "No image loaded", elem_id="image_path")
 
 
 
 
13
  img = gr.Image(type="filepath", value=get_current_image_path(), label="Blob")
14
- txt = gr.Textbox(label="Transcription")
15
  hint = gr.Markdown("*If there are multiple street names in the image, please separate them with commas.*")
16
 
17
  with gr.Row():
18
  refresh_btn = gr.Button("Retrieve Images")
19
  prev_btn = gr.Button("Previous")
20
- next_btn = gr.Button("Save & Next")
21
  del_btn = gr.Button("Delete & Next", variant="stop")
22
  #exit_btn = gr.Button("Save & Exit", variant="secondary")
23
  download_btn = gr.Button("Download Annotations")
 
24
  with gr.Row():
25
  download_file = gr.File(label="Download CSV", interactive=False)
26
 
 
27
  refresh_btn.click(
28
- fn=lambda: refresh_image_list(), # returns (image_path, text, message)
29
- outputs=[img, txt, message, image_path_display]
30
  )
31
 
32
- next_btn.click(save_and_next, inputs=txt, outputs=[img, txt, message, image_path_display])
33
- prev_btn.click(previous_image, outputs=[img, txt, message, image_path_display])
34
- del_btn.click(delete_and_next, outputs=[img, txt, message, image_path_display])
35
- #exit_btn.click(save_and_exit, inputs=txt, outputs=[img, txt, message, image_path_display])
36
- download_btn.click(lambda: get_current_annotations_path(),outputs=download_file)
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
- return [message, image_path_display, img, txt, hint,refresh_btn, prev_btn, next_btn, del_btn] #, exit_btn]
 
 
 
 
 
1
  import gradio as gr
2
  from .annotation_logic import (
3
  save_and_next, previous_image, delete_and_next, save_and_exit,
4
+ get_current_image_path, get_annotation_for_image,
5
+ get_current_annotations_path, refresh_image_list
6
  )
7
 
8
 
 
 
9
  def get_annotation_widgets():
10
  message = gr.Markdown("", visible=False)
11
+ image_path_display = gr.Markdown(
12
+ value=get_current_image_path() or "No image loaded",
13
+ elem_id="image_path"
14
+ )
15
+ progress_display = gr.Markdown(value="No images loaded", elem_id="progress_display")
16
  img = gr.Image(type="filepath", value=get_current_image_path(), label="Blob")
17
+ txt = gr.Textbox(label="Transcription", elem_id="transcription_box")
18
  hint = gr.Markdown("*If there are multiple street names in the image, please separate them with commas.*")
19
 
20
  with gr.Row():
21
  refresh_btn = gr.Button("Retrieve Images")
22
  prev_btn = gr.Button("Previous")
23
+ next_btn = gr.Button("Save & Next", variant="primary")
24
  del_btn = gr.Button("Delete & Next", variant="stop")
25
  #exit_btn = gr.Button("Save & Exit", variant="secondary")
26
  download_btn = gr.Button("Download Annotations")
27
+
28
  with gr.Row():
29
  download_file = gr.File(label="Download CSV", interactive=False)
30
 
31
+ # === Button wiring with progress_display as last output ===
32
  refresh_btn.click(
33
+ fn=refresh_image_list,
34
+ outputs=[img, txt, message, image_path_display, progress_display]
35
  )
36
 
37
+ next_btn.click(
38
+ save_and_next,
39
+ inputs=txt,
40
+ outputs=[img, txt, message, image_path_display, progress_display]
41
+ )
42
 
43
+ prev_btn.click(
44
+ previous_image,
45
+ outputs=[img, txt, message, image_path_display, progress_display]
46
+ )
47
+
48
+ del_btn.click(
49
+ delete_and_next,
50
+ outputs=[img, txt, message, image_path_display, progress_display]
51
+ )
52
+
53
+ #exit_btn.click(save_and_exit, inputs=txt, outputs=[img, txt, message, image_path_display, progress_display])
54
+
55
+ download_btn.click(
56
+ lambda: get_current_annotations_path(),
57
+ outputs=download_file
58
+ )
59
 
60
+ return [
61
+ message, image_path_display, progress_display, img, txt, hint,
62
+ refresh_btn, prev_btn, next_btn, del_btn
63
+ #, exit_btn
64
+ ]
app.py CHANGED
@@ -16,8 +16,27 @@ with gr.Blocks() as demo:
16
  get_inference_widgets(run_inference,georefImg)
17
  with gr.Tab("Annotation"):
18
  get_annotation_widgets()
19
- with gr.Tab("Map"):
20
- get_map_widgets()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  # [DEBUG]
23
  demo.launch(inbrowser=True)
 
16
  get_inference_widgets(run_inference,georefImg)
17
  with gr.Tab("Annotation"):
18
  get_annotation_widgets()
19
+ #with gr.Tab("Map"):
20
+ # get_map_widgets()
21
+
22
+
23
+ # Autofocus JavaScript: put cursor into Transcription box
24
+ demo.load(
25
+ None,
26
+ None,
27
+ _js="""
28
+ () => {
29
+ setTimeout(() => {
30
+ const el = document.getElementById('transcription_box');
31
+ if (el) {
32
+ el.querySelector('textarea').focus();
33
+ return;
34
+ }
35
+ }, 100);
36
+ }
37
+ """
38
+ )
39
+
40
 
41
  # [DEBUG]
42
  demo.launch(inbrowser=True)