muk42 commited on
Commit
6911988
·
1 Parent(s): 31faa4d

added tiling num to annotations

Browse files
annotation_tab/annotation_logic.py CHANGED
@@ -3,25 +3,16 @@ import pandas as pd
3
  import threading
4
  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
- # ==== HELPER COUNTER ====
13
- def get_progress_text():
14
- if not all_images_paths:
15
- return "No images loaded"
16
- return f"Image {current_index + 1} of {len(all_images_paths)}"
17
 
18
  # ==== STATE ====
19
- if os.path.exists(CSV_FILE):
20
- df_annotations = pd.read_csv(CSV_FILE, dtype={"blob_id": str, "human_ocr": str})
21
- else:
22
- df_annotations = pd.DataFrame(columns=["blob_id", "human_ocr"])
23
- df_annotations.to_csv(CSV_FILE, index=False)
24
-
25
  all_images = [
26
  f for f in os.listdir(IMAGE_FOLDER)
27
  if f.lower().endswith(('.png', '.jpg', '.jpeg')) and '_margin' in f
@@ -29,34 +20,66 @@ all_images = [
29
  all_images_paths = [os.path.join(IMAGE_FOLDER, f) for f in all_images]
30
  current_index = 0
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  # ==== HELPERS ====
 
 
 
 
 
33
  def get_current_image_path():
34
  if 0 <= current_index < len(all_images_paths):
35
  return all_images_paths[current_index]
36
  return None
37
 
38
- def is_annotated(image_path):
39
- """Return True if the image has any non-empty annotation, including 'DELETED'."""
 
 
 
 
 
 
 
40
  blob_id = os.path.basename(image_path).replace("_margin", "")
41
  row = df_annotations[df_annotations["blob_id"] == blob_id]
42
  if not row.empty:
43
  val = str(row["human_ocr"].values[-1]).strip()
44
- return val != "" # counts "DELETED" as annotated
45
  return False
46
 
47
- def get_annotation_for_image(image_path):
48
  blob_id = os.path.basename(image_path).replace("_margin", "")
49
  row = df_annotations[df_annotations["blob_id"] == blob_id]
50
  if not row.empty:
51
- return str(row["human_ocr"].values[-1])
52
- return ""
 
 
 
 
53
 
54
- def find_next_unannotated_index(start):
 
55
  n = len(all_images_paths)
56
  idx = start
57
  for _ in range(n):
58
  idx = (idx + 1) % n
59
- if not is_annotated(all_images_paths[idx]):
60
  return idx
61
  return None
62
 
@@ -64,7 +87,6 @@ def find_next_unannotated_index(start):
64
  def save_annotation(user_text):
65
  """Save the current annotation for the active image."""
66
  global df_annotations
67
-
68
  img_path = get_current_image_path()
69
  if not img_path:
70
  return
@@ -90,15 +112,14 @@ def save_and_next(user_text):
90
 
91
  save_annotation(user_text)
92
 
93
- # Check if all images are annotated
94
- if all(is_annotated(p) for p in all_images_paths):
95
  current_index = 0
96
  img_path = get_current_image_path()
97
  annotation = get_annotation_for_image(img_path)
98
  return img_path, annotation, gr.update(visible=True, value="All images annotated."), img_path, get_progress_text()
99
 
100
- next_idx = find_next_unannotated_index(current_index)
101
- current_index = next_idx
102
  img_path = get_current_image_path()
103
  annotation = get_annotation_for_image(img_path)
104
  return img_path, annotation, gr.update(visible=False), img_path, get_progress_text()
@@ -116,7 +137,6 @@ def previous_image():
116
  def delete_and_next():
117
  """Mark current image as DELETED and move to next image."""
118
  global current_index, df_annotations
119
-
120
  img_path = get_current_image_path()
121
  if not img_path:
122
  return None, "", gr.update(visible=True, value="No images available."), "No image loaded", "No images loaded"
@@ -133,41 +153,29 @@ def delete_and_next():
133
 
134
  df_annotations.to_csv(CSV_FILE, index=False)
135
 
136
- # Check if all images are annotated
137
- if all(is_annotated(p) for p in all_images_paths):
138
  current_index = 0
139
  img_path = get_current_image_path()
140
  annotation = get_annotation_for_image(img_path)
141
  return img_path, annotation, gr.update(visible=True, value="All images annotated."), img_path, get_progress_text()
142
 
143
- # Otherwise, move to next unannotated image
144
- next_idx = find_next_unannotated_index(current_index)
145
- if next_idx is not None:
146
- current_index = next_idx
147
- else:
148
- current_index = 0
149
-
150
  img_path = get_current_image_path()
151
  annotation = get_annotation_for_image(img_path)
152
  return img_path, annotation, gr.update(visible=False), img_path, get_progress_text()
153
 
154
-
155
  def save_and_exit(user_text):
156
  if get_current_image_path() is not None:
157
  save_annotation(user_text)
158
  threading.Timer(1, lambda: os._exit(0)).start()
159
  return None, "", gr.update(visible=True, value="Session closed."), "", get_progress_text()
160
 
161
- def get_current_annotations_path():
162
- return CSV_FILE
163
-
164
  def refresh_image_list():
165
- """Reload images and reset CSV and state."""
166
  global all_images_paths, current_index, df_annotations
167
 
168
- df_annotations = pd.DataFrame(columns=["blob_id", "human_ocr"])
169
- df_annotations.to_csv(CSV_FILE, index=False)
170
-
171
  all_images = [
172
  f for f in os.listdir(IMAGE_FOLDER)
173
  if f.lower().endswith(('.png', '.jpg', '.jpeg')) and '_margin' in f
@@ -175,9 +183,15 @@ def refresh_image_list():
175
  all_images_paths = [os.path.join(IMAGE_FOLDER, f) for f in all_images]
176
  current_index = 0
177
 
 
 
 
 
 
178
  if not all_images_paths:
179
  return None, "", gr.update(visible=True, value="No images available."), "No image loaded", "No images loaded"
180
 
 
181
  img_path = get_current_image_path()
182
  annotation = get_annotation_for_image(img_path)
183
  return img_path, annotation, gr.update(visible=False), img_path, get_progress_text()
 
3
  import threading
4
  import gradio as gr
5
  from config import OUTPUT_DIR
6
+ import re
7
 
8
  # ==== CONFIG ====
9
  IMAGE_FOLDER = os.path.join(OUTPUT_DIR, "blobs")
10
  os.makedirs(IMAGE_FOLDER, exist_ok=True)
 
11
 
12
+ CSV_FILE = None
13
+ df_annotations = pd.DataFrame(columns=["blob_id", "human_ocr"])
 
 
 
14
 
15
  # ==== STATE ====
 
 
 
 
 
 
16
  all_images = [
17
  f for f in os.listdir(IMAGE_FOLDER)
18
  if f.lower().endswith(('.png', '.jpg', '.jpeg')) and '_margin' in f
 
20
  all_images_paths = [os.path.join(IMAGE_FOLDER, f) for f in all_images]
21
  current_index = 0
22
 
23
+ # ==== TILE CSV ====
24
+ def switch_tile_csv(selected_tile):
25
+ global CSV_FILE, df_annotations
26
+ tile_filename = os.path.basename(selected_tile["tile_path"])
27
+ tile_name, _ = os.path.splitext(tile_filename)
28
+ CSV_FILE = os.path.join(OUTPUT_DIR, f"annotations_{tile_name}.csv")
29
+
30
+ if os.path.exists(CSV_FILE):
31
+ df_annotations = pd.read_csv(CSV_FILE, dtype={"blob_id": str, "human_ocr": str})
32
+ else:
33
+ df_annotations = pd.DataFrame(columns=["blob_id", "human_ocr"])
34
+ df_annotations.to_csv(CSV_FILE, index=False)
35
+
36
+ return CSV_FILE
37
+
38
  # ==== HELPERS ====
39
+ def get_progress_text():
40
+ if not all_images_paths:
41
+ return "No images loaded"
42
+ return f"Image {current_index + 1} of {len(all_images_paths)}"
43
+
44
  def get_current_image_path():
45
  if 0 <= current_index < len(all_images_paths):
46
  return all_images_paths[current_index]
47
  return None
48
 
49
+ def get_annotation_for_image(image_path):
50
+ blob_id = os.path.basename(image_path).replace("_margin", "")
51
+ row = df_annotations[df_annotations["blob_id"] == blob_id]
52
+ if not row.empty:
53
+ return str(row["human_ocr"].values[-1])
54
+ return ""
55
+
56
+ def is_annotated_or_deleted(image_path):
57
+ """Return True if image has an annotation or is deleted."""
58
  blob_id = os.path.basename(image_path).replace("_margin", "")
59
  row = df_annotations[df_annotations["blob_id"] == blob_id]
60
  if not row.empty:
61
  val = str(row["human_ocr"].values[-1]).strip()
62
+ return val != "" # includes 'DELETED' as counted
63
  return False
64
 
65
+ def is_deleted(image_path):
66
  blob_id = os.path.basename(image_path).replace("_margin", "")
67
  row = df_annotations[df_annotations["blob_id"] == blob_id]
68
  if not row.empty:
69
+ return str(row["human_ocr"].values[-1]).strip() == "DELETED"
70
+ return False
71
+
72
+ def all_processed():
73
+ """Return True if all images are either annotated or deleted."""
74
+ return all(is_annotated_or_deleted(p) for p in all_images_paths)
75
 
76
+ def find_next_unprocessed_index(start):
77
+ """Return the next image index that is neither annotated nor deleted."""
78
  n = len(all_images_paths)
79
  idx = start
80
  for _ in range(n):
81
  idx = (idx + 1) % n
82
+ if not is_annotated_or_deleted(all_images_paths[idx]):
83
  return idx
84
  return None
85
 
 
87
  def save_annotation(user_text):
88
  """Save the current annotation for the active image."""
89
  global df_annotations
 
90
  img_path = get_current_image_path()
91
  if not img_path:
92
  return
 
112
 
113
  save_annotation(user_text)
114
 
115
+ if all_processed():
 
116
  current_index = 0
117
  img_path = get_current_image_path()
118
  annotation = get_annotation_for_image(img_path)
119
  return img_path, annotation, gr.update(visible=True, value="All images annotated."), img_path, get_progress_text()
120
 
121
+ next_idx = find_next_unprocessed_index(current_index)
122
+ current_index = next_idx if next_idx is not None else 0
123
  img_path = get_current_image_path()
124
  annotation = get_annotation_for_image(img_path)
125
  return img_path, annotation, gr.update(visible=False), img_path, get_progress_text()
 
137
  def delete_and_next():
138
  """Mark current image as DELETED and move to next image."""
139
  global current_index, df_annotations
 
140
  img_path = get_current_image_path()
141
  if not img_path:
142
  return None, "", gr.update(visible=True, value="No images available."), "No image loaded", "No images loaded"
 
153
 
154
  df_annotations.to_csv(CSV_FILE, index=False)
155
 
156
+ if all_processed():
 
157
  current_index = 0
158
  img_path = get_current_image_path()
159
  annotation = get_annotation_for_image(img_path)
160
  return img_path, annotation, gr.update(visible=True, value="All images annotated."), img_path, get_progress_text()
161
 
162
+ next_idx = find_next_unprocessed_index(current_index)
163
+ current_index = next_idx if next_idx is not None else 0
 
 
 
 
 
164
  img_path = get_current_image_path()
165
  annotation = get_annotation_for_image(img_path)
166
  return img_path, annotation, gr.update(visible=False), img_path, get_progress_text()
167
 
 
168
  def save_and_exit(user_text):
169
  if get_current_image_path() is not None:
170
  save_annotation(user_text)
171
  threading.Timer(1, lambda: os._exit(0)).start()
172
  return None, "", gr.update(visible=True, value="Session closed."), "", get_progress_text()
173
 
 
 
 
174
  def refresh_image_list():
175
+ """Reload images for the current tile and clear the CSV."""
176
  global all_images_paths, current_index, df_annotations
177
 
178
+ # Reload images
 
 
179
  all_images = [
180
  f for f in os.listdir(IMAGE_FOLDER)
181
  if f.lower().endswith(('.png', '.jpg', '.jpeg')) and '_margin' in f
 
183
  all_images_paths = [os.path.join(IMAGE_FOLDER, f) for f in all_images]
184
  current_index = 0
185
 
186
+ # Reset CSV for current tile
187
+ df_annotations = pd.DataFrame(columns=["blob_id", "human_ocr"])
188
+ if CSV_FILE:
189
+ df_annotations.to_csv(CSV_FILE, index=False)
190
+
191
  if not all_images_paths:
192
  return None, "", gr.update(visible=True, value="No images available."), "No image loaded", "No images loaded"
193
 
194
+ # Return first image
195
  img_path = get_current_image_path()
196
  annotation = get_annotation_for_image(img_path)
197
  return img_path, annotation, gr.update(visible=False), img_path, get_progress_text()
annotation_tab/annotation_setup.py CHANGED
@@ -1,12 +1,14 @@
 
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",
@@ -53,8 +55,10 @@ def get_annotation_widgets():
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 [
 
1
+ import os
2
  import gradio as gr
3
+ import pandas as pd
4
+ from config import OUTPUT_DIR
5
  from .annotation_logic import (
6
  save_and_next, previous_image, delete_and_next, save_and_exit,
7
  get_current_image_path, get_annotation_for_image,
8
+ refresh_image_list, switch_tile_csv
9
  )
10
 
11
+ def get_annotation_widgets(selected_tile_state):
 
12
  message = gr.Markdown("", visible=False)
13
  image_path_display = gr.Markdown(
14
  value=get_current_image_path() or "No image loaded",
 
55
  #exit_btn.click(save_and_exit, inputs=txt, outputs=[img, txt, message, image_path_display, progress_display])
56
 
57
  download_btn.click(
58
+ #lambda: get_current_annotations_path(),
59
+ fn=lambda selected_tile: switch_tile_csv(selected_tile),
60
+ inputs=[selected_tile_state],
61
+ outputs=[download_file]
62
  )
63
 
64
  return [
app.py CHANGED
@@ -15,10 +15,11 @@ logging.basicConfig(level=logging.DEBUG)
15
 
16
 
17
  with gr.Blocks() as demo:
 
18
  with gr.Tab("Inference"):
19
- image_input, gcp_input, city_name,user_crs, score_th, hist_th, hist_dic, run_button, output, download_file = get_inference_widgets(run_inference,georefImg)
20
  with gr.Tab("Annotation"):
21
- get_annotation_widgets()
22
  with gr.Tab("Map"):
23
  get_map_widgets(city_name)
24
 
 
15
 
16
 
17
  with gr.Blocks() as demo:
18
+ selected_tile_state=gr.State(value=None)
19
  with gr.Tab("Inference"):
20
+ image_input, gcp_input, city_name,user_crs, score_th, hist_th, hist_dic, run_button, output, download_file = get_inference_widgets(run_inference,georefImg, selected_tile_state)
21
  with gr.Tab("Annotation"):
22
+ get_annotation_widgets(selected_tile_state)
23
  with gr.Tab("Map"):
24
  get_map_widgets(city_name)
25
 
inference_tab/inference_setup.py CHANGED
@@ -77,7 +77,7 @@ def enable_textbox(file):
77
  return gr.update(interactive=bool(file))
78
 
79
 
80
- def get_inference_widgets(run_inference,georefImg):
81
  with gr.Row():
82
  # Left column
83
  with gr.Column(scale=1,min_width=500):
@@ -120,7 +120,8 @@ def get_inference_widgets(run_inference,georefImg):
120
  file_types=[".csv"],
121
  type="filepath")
122
 
123
- selected_tile_path = gr.State()
 
124
 
125
 
126
  # Wire events
@@ -129,12 +130,12 @@ def get_inference_widgets(run_inference,georefImg):
129
  outputs=[annotated_out, run_button]
130
  )
131
  annotated_out.select(
132
- fn=select_tile, inputs=[selected_tile_path],
133
- outputs=[selected_tile, run_button, selected_tile_path]
134
  )
135
  run_button.click(
136
  fn=run_inference,
137
- inputs=[selected_tile_path, gcp_input,user_crs, city_name, score_th, hist_th,hist_dic],
138
  outputs=[output, download_file]
139
  )
140
 
 
77
  return gr.update(interactive=bool(file))
78
 
79
 
80
+ def get_inference_widgets(run_inference,georefImg,selected_tile_state):
81
  with gr.Row():
82
  # Left column
83
  with gr.Column(scale=1,min_width=500):
 
120
  file_types=[".csv"],
121
  type="filepath")
122
 
123
+ # pass globally instead
124
+ #selected_tile_state = gr.State()
125
 
126
 
127
  # Wire events
 
130
  outputs=[annotated_out, run_button]
131
  )
132
  annotated_out.select(
133
+ fn=select_tile, inputs=[selected_tile_state],
134
+ outputs=[selected_tile, run_button, selected_tile_state]
135
  )
136
  run_button.click(
137
  fn=run_inference,
138
+ inputs=[selected_tile_state, gcp_input,user_crs, city_name, score_th, hist_th,hist_dic],
139
  outputs=[output, download_file]
140
  )
141