MichaelDeutges commited on
Commit
0a07697
·
verified ·
1 Parent(s): 83692dc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +133 -85
app.py CHANGED
@@ -1,89 +1,137 @@
1
- import gradio as gr
2
  import os
 
 
 
 
 
3
  import pandas as pd
4
  from PIL import Image
5
- import random
6
-
7
- IMAGE_FOLDER = "images"
8
- LABELS_FILE = "labels.csv"
9
-
10
- # Load or create labels dataframe
11
- if os.path.exists(LABELS_FILE):
12
- labels_df = pd.read_csv(LABELS_FILE)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  else:
14
- labels_df = pd.DataFrame(columns=["user", "image", "label"])
15
-
16
- def get_images():
17
- valid_exts = {".jpg", ".jpeg", ".png", ".bmp", ".gif", ".tif", ".tiff", ".webp"}
18
- all_files = []
19
- for root, _, files in os.walk(IMAGE_FOLDER):
20
- for f in files:
21
- if os.path.splitext(f.lower())[1] in valid_exts:
22
- all_files.append(os.path.join(root, f))
23
- return sorted(all_files)
24
-
25
- def start_labeling(user):
26
- images = get_images()
27
- if not images:
28
- return None, f"No images found in '{IMAGE_FOLDER}'", gr.update(visible=False)
29
- random.shuffle(images)
30
- # Store the shuffled list in session state
31
- return images[0], f"Found {len(images)} images. Starting labeling...", gr.update(visible=True), images
32
-
33
- def label_image(user, current_image, label, remaining_images):
34
- global labels_df
35
- labels_df = pd.concat([labels_df, pd.DataFrame([[user, current_image, label]], columns=labels_df.columns)], ignore_index=True)
36
- labels_df.to_csv(LABELS_FILE, index=False)
37
-
38
- if not remaining_images:
39
- return None, "All images labeled!", gr.update(visible=False), []
40
-
41
- next_image = remaining_images.pop(0)
42
- return next_image, f"{len(remaining_images)} images remaining.", gr.update(visible=True), remaining_images
43
-
44
- with gr.Blocks() as demo:
45
- gr.Markdown("# 🏷️ Two-Button Image Labeler")
46
-
47
- with gr.Row():
48
- user_input = gr.Textbox(label="Your name", placeholder="Enter your name")
49
- start_btn = gr.Button("Start Labeling", variant="primary")
50
-
51
- info = gr.Markdown()
52
- image_display = gr.Image(type="filepath", label="Image to label")
53
- with gr.Row(visible=False) as button_row:
54
- left_btn = gr.Button("⬅️ Class A")
55
- skip_btn = gr.Button("Skip")
56
- right_btn = gr.Button("Class B ➡️")
57
-
58
- state_images = gr.State([])
59
-
60
- start_btn.click(
61
- fn=lambda user: start_labeling(user),
62
- inputs=user_input,
63
- outputs=[image_display, info, button_row, state_images]
64
- )
65
-
66
- left_btn.click(
67
- fn=lambda user, img, rem: label_image(user, img, "Class A", rem),
68
- inputs=[user_input, image_display, state_images],
69
- outputs=[image_display, info, button_row, state_images]
70
- )
71
-
72
- right_btn.click(
73
- fn=lambda user, img, rem: label_image(user, img, "Class B", rem),
74
- inputs=[user_input, image_display, state_images],
75
- outputs=[image_display, info, button_row, state_images]
76
- )
77
-
78
- skip_btn.click(
79
- fn=lambda user, img, rem: label_image(user, img, "Skip", rem),
80
- inputs=[user_input, image_display, state_images],
81
- outputs=[image_display, info, button_row, state_images]
82
- )
83
-
84
-
85
- demo = demo.queue(max_size=64)
86
-
87
- if __name__ == "__main__":
88
- demo.launch(server_name="0.0.0.0", server_port=7860, show_api=False)
89
-
 
 
1
  import os
2
+ import glob
3
+ from pathlib import Path
4
+ from datetime import datetime, timezone
5
+
6
+ import streamlit as st
7
  import pandas as pd
8
  from PIL import Image
9
+ from filelock import FileLock
10
+
11
+ # ---------------- Config ----------------
12
+ IMAGE_DIR = os.getenv("IMAGE_DIR", "images")
13
+ LABELS_CSV = os.getenv("LABELS_CSV", "labels.csv")
14
+ LABEL_LEFT = os.getenv("LABEL_A", "Class A")
15
+ LABEL_RIGHT = os.getenv("LABEL_B", "Class B")
16
+ SHOW_ALREADY_LABELED = os.getenv("SHOW_ALREADY_LABELED", "0") == "1"
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 = []
24
+ for p in glob.glob(os.path.join(IMAGE_DIR, "**", "*"), recursive=True):
25
+ if p.lower().endswith(SUPPORTED_EXTS):
26
+ paths.append(p)
27
+ return sorted(paths)
28
+
29
+ def read_labels():
30
+ if os.path.exists(LABELS_CSV):
31
+ try:
32
+ return pd.read_csv(LABELS_CSV)
33
+ except Exception:
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(os.path.dirname(LABELS_CSV) or ".", 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
+ annotator = st.text_input("Your name*", value="")
62
+ start = st.button("Start", type="primary")
63
+
64
+ # session state
65
+ if "order" not in st.session_state:
66
+ st.session_state.order = []
67
+ if "idx" not in st.session_state:
68
+ st.session_state.idx = 0
69
+ if "total" not in st.session_state:
70
+ st.session_state.total = 0
71
+ if "started" not in st.session_state:
72
+ st.session_state.started = False
73
+
74
+ # start
75
+ if start:
76
+ if not annotator.strip():
77
+ st.sidebar.error("Please enter your name.")
78
+ else:
79
+ imgs = list_images()
80
+ labels_df = read_labels()
81
+ if not SHOW_ALREADY_LABELED and len(labels_df) > 0:
82
+ labeled = set(labels_df["image"].astype(str).tolist())
83
+ rel_imgs = [rel_to_image_dir(p) for p in imgs]
84
+ imgs = [p for p, r in zip(imgs, rel_imgs) if r not in labeled]
85
+
86
+ if not imgs:
87
+ st.warning("No images found (or all labeled). Upload PNG/JPG/etc. to the `images/` folder.")
88
+ else:
89
+ # Simple deterministic order; you can randomize with: random.shuffle(imgs)
90
+ st.session_state.order = imgs
91
+ st.session_state.idx = 0
92
+ st.session_state.total = len(imgs)
93
+ st.session_state.started = True
94
+
95
+ # main panel
96
+ if not st.session_state.started:
97
+ st.info("Fill your name on the left and press **Start**.")
98
  else:
99
+ idx = st.session_state.idx
100
+ total = st.session_state.total
101
+ if idx >= total:
102
+ st.success("All done 🎉 Thank you!")
103
+ else:
104
+ current_image = st.session_state.order[idx]
105
+ st.caption(f"{idx} / {total}")
106
+ try:
107
+ img = Image.open(current_image)
108
+ # If multipage TIFF, show first page
109
+ if getattr(img, "n_frames", 1) > 1:
110
+ img.seek(0)
111
+ # Convert to RGB for browsers
112
+ if img.mode not in ("RGB", "RGBA"):
113
+ img = img.convert("RGB")
114
+ st.image(img, use_column_width=True)
115
+ except Exception as e:
116
+ st.warning(f"Could not display image: {current_image}\n{e}")
117
+
118
+ col1, col2, col3 = st.columns([1,1,1])
119
+ with col1:
120
+ if st.button(f"⬅️ {LABEL_LEFT}", use_container_width=True):
121
+ write_label(current_image, LABEL_LEFT, annotator.strip())
122
+ st.session_state.idx += 1
123
+ st.rerun()
124
+ with col2:
125
+ if st.button("Skip", use_container_width=True):
126
+ write_label(current_image, "Skip", annotator.strip())
127
+ st.session_state.idx += 1
128
+ st.rerun()
129
+ with col3:
130
+ if st.button(f"{LABEL_RIGHT} ➡️", use_container_width=True):
131
+ write_label(current_image, LABEL_RIGHT, annotator.strip())
132
+ st.session_state.idx += 1
133
+ st.rerun()
134
+
135
+ # Footer / admin
136
+ st.divider()
137
+ st.caption("Labels are saved to `labels.csv` in this Space. You can download it from the Files tab.")