MichaelDeutges commited on
Commit
8ec9286
·
verified ·
1 Parent(s): 2a08bd4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +26 -47
app.py CHANGED
@@ -9,17 +9,15 @@ from PIL import Image
9
  from filelock import FileLock
10
 
11
  # ---------------- Config ----------------
12
- BASE_DIR = Path(__file__).parent.resolve()
13
  IMAGE_DIR = os.getenv("IMAGE_DIR", "images")
14
  LABELS_CSV = os.getenv("LABELS_CSV", str(BASE_DIR / "labels.csv"))
15
  LABEL_LEFT = os.getenv("LABEL_A", "Class A")
16
  LABEL_RIGHT = os.getenv("LABEL_B", "Class B")
17
- SHOW_ALREADY_LABELED = os.getenv("SHOW_ALREADY_LABELED", "0") == "1"
18
  SUPPORTED_EXTS = (".png", ".jpg", ".jpeg", ".bmp", ".gif", ".tif", ".tiff", ".webp")
19
 
20
  st.set_page_config(page_title="Two-Button Image Labeler", layout="centered")
21
 
22
-
23
  # --------------- Helpers ----------------
24
  def list_images():
25
  paths = []
@@ -28,7 +26,6 @@ def list_images():
28
  paths.append(p)
29
  return sorted(paths)
30
 
31
-
32
  def read_labels():
33
  if os.path.exists(LABELS_CSV):
34
  try:
@@ -37,46 +34,38 @@ def read_labels():
37
  return pd.DataFrame(columns=["image", "label", "annotator", "timestamp"])
38
  return pd.DataFrame(columns=["image", "label", "annotator", "timestamp"])
39
 
40
-
41
  def rel_to_image_dir(p: str):
42
  try:
43
  return str(Path(p).resolve().relative_to(Path(IMAGE_DIR).resolve()))
44
  except Exception:
45
  return p
46
 
47
-
48
  def write_label(image_path: str, label: str, annotator: str):
49
- os.makedirs(os.path.dirname(LABELS_CSV) or ".", exist_ok=True)
50
  record = {
51
  "image": rel_to_image_dir(image_path),
52
  "label": label,
53
  "annotator": annotator,
54
- "timestamp": datetime.now(timezone.utc).isoformat(),
55
  }
56
  with FileLock(LABELS_CSV + ".lock", timeout=10):
57
  exists = os.path.exists(LABELS_CSV)
58
  df = pd.DataFrame([record])
59
  df.to_csv(LABELS_CSV, mode="a", header=not exists, index=False)
60
 
61
-
62
  # --------------- UI ---------------------
63
  st.title("🏷️ Two-Button Image Labeler")
64
- st.write(
65
- "Enter your name, click **Start**, then label each image using the big buttons. "
66
- "Use **Skip** to bypass an image."
67
- )
68
 
69
  with st.sidebar:
70
  default_name = st.session_state.get("annotator", "")
71
- annotator = st.text_input(
72
- "Your name*", value=default_name, placeholder="e.g., Dr. Smith"
73
- )
74
 
75
  c1, c2 = st.columns(2)
76
  with c1:
77
  start_btn = st.button("Start", type="primary")
78
  with c2:
79
- restart_btn = st.button("Restart")
80
 
81
  continue_by_name = st.toggle(
82
  "Only show images I haven't labeled yet",
@@ -93,52 +82,40 @@ if "total" not in st.session_state:
93
  st.session_state.total = 0
94
  if "started" not in st.session_state:
95
  st.session_state.started = False
 
 
96
 
97
- # Restart clears session
98
- if restart_btn:
99
  st.session_state.order = []
100
  st.session_state.idx = 0
101
  st.session_state.total = 0
102
  st.session_state.started = False
 
103
  st.rerun()
104
 
105
- # Start flow
106
  if start_btn:
107
  if not annotator.strip():
108
  st.sidebar.error("Please enter your name.")
109
  else:
110
- st.session_state["annotator"] = annotator.strip()
111
  imgs = list_images()
112
  labels_df = read_labels()
113
- rel_imgs = [rel_to_image_dir(p) for p in imgs]
114
-
115
- if continue_by_name:
116
- # only show images THIS annotator hasn't labeled yet
117
- done_by_me = set(
118
- labels_df.query("annotator == @annotator.strip()")["image"].astype(str)
119
- )
120
- imgs = [p for p, r in zip(imgs, rel_imgs) if r not in done_by_me]
121
 
122
- elif not SHOW_ALREADY_LABELED:
123
- # hide anything labeled by anyone
124
- done_any = set(labels_df["image"].astype(str))
125
- imgs = [p for p, r in zip(imgs, rel_imgs) if r not in done_any]
 
126
 
127
  if not imgs:
128
- if continue_by_name:
129
- st.success(
130
- f"All caught up for **{annotator.strip()}** 🎉 Nothing left to label."
131
- )
132
- else:
133
- st.warning(
134
- "No images found (or all labeled). Upload PNG/JPG/etc. to the `images/` folder."
135
- )
136
  else:
137
  st.session_state.order = imgs
138
  st.session_state.idx = 0
139
  st.session_state.total = len(imgs)
140
  st.session_state.started = True
141
- st.rerun()
142
 
143
  # main panel
144
  if not st.session_state.started:
@@ -153,28 +130,30 @@ else:
153
  st.caption(f"{idx} / {total}")
154
  try:
155
  img = Image.open(current_image)
 
156
  if getattr(img, "n_frames", 1) > 1:
157
  img.seek(0)
 
158
  if img.mode not in ("RGB", "RGBA"):
159
  img = img.convert("RGB")
160
- st.image(img, use_column_width=True)
161
  except Exception as e:
162
  st.warning(f"Could not display image: {current_image}\n{e}")
163
 
164
- col1, col2, col3 = st.columns([1, 1, 1])
165
  with col1:
166
  if st.button(f"⬅️ {LABEL_LEFT}", use_container_width=True):
167
- write_label(current_image, LABEL_LEFT, annotator.strip())
168
  st.session_state.idx += 1
169
  st.rerun()
170
  with col2:
171
  if st.button("Skip", use_container_width=True):
172
- write_label(current_image, "Skip", annotator.strip())
173
  st.session_state.idx += 1
174
  st.rerun()
175
  with col3:
176
  if st.button(f"{LABEL_RIGHT} ➡️", use_container_width=True):
177
- write_label(current_image, LABEL_RIGHT, annotator.strip())
178
  st.session_state.idx += 1
179
  st.rerun()
180
 
 
9
  from filelock import FileLock
10
 
11
  # ---------------- Config ----------------
12
+ BASE_DIR = Path(".").resolve() # Space root
13
  IMAGE_DIR = os.getenv("IMAGE_DIR", "images")
14
  LABELS_CSV = os.getenv("LABELS_CSV", str(BASE_DIR / "labels.csv"))
15
  LABEL_LEFT = os.getenv("LABEL_A", "Class A")
16
  LABEL_RIGHT = os.getenv("LABEL_B", "Class B")
 
17
  SUPPORTED_EXTS = (".png", ".jpg", ".jpeg", ".bmp", ".gif", ".tif", ".tiff", ".webp")
18
 
19
  st.set_page_config(page_title="Two-Button Image Labeler", layout="centered")
20
 
 
21
  # --------------- Helpers ----------------
22
  def list_images():
23
  paths = []
 
26
  paths.append(p)
27
  return sorted(paths)
28
 
 
29
  def read_labels():
30
  if os.path.exists(LABELS_CSV):
31
  try:
 
34
  return pd.DataFrame(columns=["image", "label", "annotator", "timestamp"])
35
  return pd.DataFrame(columns=["image", "label", "annotator", "timestamp"])
36
 
 
37
  def rel_to_image_dir(p: str):
38
  try:
39
  return str(Path(p).resolve().relative_to(Path(IMAGE_DIR).resolve()))
40
  except Exception:
41
  return p
42
 
 
43
  def write_label(image_path: str, label: str, annotator: str):
44
+ os.makedirs(Path(LABELS_CSV).parent, exist_ok=True)
45
  record = {
46
  "image": rel_to_image_dir(image_path),
47
  "label": label,
48
  "annotator": annotator,
49
+ "timestamp": datetime.now(timezone.utc).isoformat()
50
  }
51
  with FileLock(LABELS_CSV + ".lock", timeout=10):
52
  exists = os.path.exists(LABELS_CSV)
53
  df = pd.DataFrame([record])
54
  df.to_csv(LABELS_CSV, mode="a", header=not exists, index=False)
55
 
 
56
  # --------------- UI ---------------------
57
  st.title("🏷️ Two-Button Image Labeler")
58
+ st.write("Enter your name, click **Start**, then label each image using the big buttons. Use **Skip** to bypass an image.")
 
 
 
59
 
60
  with st.sidebar:
61
  default_name = st.session_state.get("annotator", "")
62
+ annotator = st.text_input("Your name*", value=default_name, placeholder="e.g., Dr. Smith")
 
 
63
 
64
  c1, c2 = st.columns(2)
65
  with c1:
66
  start_btn = st.button("Start", type="primary")
67
  with c2:
68
+ restart_req = st.button("Restart")
69
 
70
  continue_by_name = st.toggle(
71
  "Only show images I haven't labeled yet",
 
82
  st.session_state.total = 0
83
  if "started" not in st.session_state:
84
  st.session_state.started = False
85
+ if "annotator" not in st.session_state:
86
+ st.session_state.annotator = ""
87
 
88
+ # restart clears state
89
+ if restart_req:
90
  st.session_state.order = []
91
  st.session_state.idx = 0
92
  st.session_state.total = 0
93
  st.session_state.started = False
94
+ st.session_state.annotator = ""
95
  st.rerun()
96
 
97
+ # start
98
  if start_btn:
99
  if not annotator.strip():
100
  st.sidebar.error("Please enter your name.")
101
  else:
102
+ st.session_state.annotator = annotator.strip()
103
  imgs = list_images()
104
  labels_df = read_labels()
 
 
 
 
 
 
 
 
105
 
106
+ # Already labeled by this annotator?
107
+ if continue_by_name and len(labels_df) > 0:
108
+ labeled = set(labels_df.query("annotator == @annotator")["image"].astype(str).tolist())
109
+ rel_imgs = [rel_to_image_dir(p) for p in imgs]
110
+ imgs = [p for p, r in zip(imgs, rel_imgs) if r not in labeled]
111
 
112
  if not imgs:
113
+ st.warning("No images found (or all labeled by you). Upload PNG/JPG/etc. to the `images/` folder.")
 
 
 
 
 
 
 
114
  else:
115
  st.session_state.order = imgs
116
  st.session_state.idx = 0
117
  st.session_state.total = len(imgs)
118
  st.session_state.started = True
 
119
 
120
  # main panel
121
  if not st.session_state.started:
 
130
  st.caption(f"{idx} / {total}")
131
  try:
132
  img = Image.open(current_image)
133
+ # If multipage TIFF, show first page
134
  if getattr(img, "n_frames", 1) > 1:
135
  img.seek(0)
136
+ # Convert to RGB for browsers
137
  if img.mode not in ("RGB", "RGBA"):
138
  img = img.convert("RGB")
139
+ st.image(img, use_container_width=True)
140
  except Exception as e:
141
  st.warning(f"Could not display image: {current_image}\n{e}")
142
 
143
+ col1, col2, col3 = st.columns([1,1,1])
144
  with col1:
145
  if st.button(f"⬅️ {LABEL_LEFT}", use_container_width=True):
146
+ write_label(current_image, LABEL_LEFT, st.session_state.annotator)
147
  st.session_state.idx += 1
148
  st.rerun()
149
  with col2:
150
  if st.button("Skip", use_container_width=True):
151
+ write_label(current_image, "Skip", st.session_state.annotator)
152
  st.session_state.idx += 1
153
  st.rerun()
154
  with col3:
155
  if st.button(f"{LABEL_RIGHT} ➡️", use_container_width=True):
156
+ write_label(current_image, LABEL_RIGHT, st.session_state.annotator)
157
  st.session_state.idx += 1
158
  st.rerun()
159