Spaces:
Sleeping
Sleeping
Upload 12 files
Browse files- .gitattributes +8 -0
- README.md +92 -9
- app.py +241 -0
- images/.gitkeep +0 -0
- images/BAS_0001.tiff +3 -0
- images/KSC_0001.tiff +3 -0
- images/LYT_0005.tiff +3 -0
- images/MOB_0002.tiff +3 -0
- images/MON_0002.tiff +3 -0
- images/MYB_0003.tiff +3 -0
- images/NGS_0002.tiff +3 -0
- images/PMO_0002.tiff +3 -0
- requirements.txt +6 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,11 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
images/BAS_0001.tiff filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
images/KSC_0001.tiff filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
images/LYT_0005.tiff filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
images/MOB_0002.tiff filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
images/MON_0002.tiff filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
images/MYB_0003.tiff filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
images/NGS_0002.tiff filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
images/PMO_0002.tiff filter=lfs diff=lfs merge=lfs -text
|
README.md
CHANGED
|
@@ -1,12 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
|
|
|
| 10 |
---
|
| 11 |
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
# Two-Button Image Labeler (HuggingFace Spaces)
|
| 3 |
+
|
| 4 |
+
A zero-install app for collaborators to label images with **two buttons** (left/right).
|
| 5 |
+
Perfect for quick yes/no or binary classification tasks with doctors or non-technical users.
|
| 6 |
+
|
| 7 |
+
## Features
|
| 8 |
+
- Shows one image at a time.
|
| 9 |
+
- Two big buttons (configurable labels).
|
| 10 |
+
- Keyboard shortcuts: **A** for left, **L** for right.
|
| 11 |
+
- "Skip" button.
|
| 12 |
+
- Captures annotator's name.
|
| 13 |
+
- Saves to `labels.csv` and (optionally) **auto-uploads** it back to your Space repository after every label.
|
| 14 |
+
- Hides already-labeled images by default (configurable).
|
| 15 |
+
- Works with multiple annotators at once (file lock ensures safe writes).
|
| 16 |
+
|
| 17 |
+
---
|
| 18 |
+
|
| 19 |
+
## Quick Start (HuggingFace Spaces)
|
| 20 |
+
|
| 21 |
+
1. Create a new **Space** → **Gradio**.
|
| 22 |
+
2. Upload the files from this repo (`app.py`, `requirements.txt`). You can use the zip attached or drag & drop.
|
| 23 |
+
3. Create a folder `images/` in the Space and upload your images there (PNG/JPG/etc.).
|
| 24 |
+
4. (Optional but recommended) Set secrets in the Space **Settings → Variables and secrets**:
|
| 25 |
+
- `HF_TOKEN` — a write token from your Hugging Face account.
|
| 26 |
+
- `HF_SPACE_REPO` — your Space repo id, e.g. `yourname/your-space`.
|
| 27 |
+
- With these set, the app will push updates to `labels.csv` back into the repo automatically so you always have the latest labels.
|
| 28 |
+
5. Share the Space link with your doctors. They:
|
| 29 |
+
- Enter their name
|
| 30 |
+
- Click **Start**
|
| 31 |
+
- Press **← Class A** or **Class B →** (or **A/L** keys)
|
| 32 |
+
|
| 33 |
+
You can download the latest labels at any time with the **Download CSV** button (Admin section).
|
| 34 |
+
|
| 35 |
+
---
|
| 36 |
+
|
| 37 |
+
## Configuration (Environment Variables)
|
| 38 |
+
|
| 39 |
+
| Variable | Default | Description |
|
| 40 |
+
|---|---|---|
|
| 41 |
+
| `IMAGE_DIR` | `images` | Folder containing images (can include subfolders). |
|
| 42 |
+
| `LABELS_CSV` | `labels.csv` | Path to the CSV file saved by the app. |
|
| 43 |
+
| `LABEL_A` | `Class A` | Text on the left button. |
|
| 44 |
+
| `LABEL_B` | `Class B` | Text on the right button. |
|
| 45 |
+
| `APP_TITLE` | `Two-Button Image Labeler` | Title at the top of the app. |
|
| 46 |
+
| `APP_DESCRIPTION` | *(see app.py)* | Instructions shown under the title. |
|
| 47 |
+
| `SHOW_ALREADY_LABELED` | `0` | If `1`, includes images that already have a label. |
|
| 48 |
+
| `SHUFFLE_IMAGES` | `1` | If `1`, randomize image order per session. |
|
| 49 |
+
| `HF_TOKEN` | *(none)* | Your HF write token. If set with `HF_SPACE_REPO`, labels are auto-pushed to the repo. |
|
| 50 |
+
| `HF_SPACE_REPO` | *(none)* | Your Space repo id, e.g. `yourname/your-space`. |
|
| 51 |
+
| `UPLOAD_TO_REPO` | `1` | If `1`, attempt to push labels to the repo on every write. |
|
| 52 |
+
| `AUTH_USER`, `AUTH_PASS` | *(none)* | If set, password-protect the app (basic auth). Useful if your Space is public. |
|
| 53 |
+
|
| 54 |
+
**Note:** If you keep `SHOW_ALREADY_LABELED=0`, the app hides any image that appears at least once in `labels.csv` (regardless of annotator). Set to `1` if you want multiple independent labels per image.
|
| 55 |
+
|
| 56 |
---
|
| 57 |
+
|
| 58 |
+
## CSV Format
|
| 59 |
+
|
| 60 |
+
The app appends one row per click with these columns:
|
| 61 |
+
- `image` — relative path to the image from `IMAGE_DIR`
|
| 62 |
+
- `label` — the chosen label (`LABEL_A` or `LABEL_B`)
|
| 63 |
+
- `annotator` — whatever the user entered under "Your name"
|
| 64 |
+
- `timestamp` — ISO 8601 UTC time
|
| 65 |
+
|
| 66 |
---
|
| 67 |
|
| 68 |
+
## Deploy Locally (Optional)
|
| 69 |
+
|
| 70 |
+
```bash
|
| 71 |
+
pip install -r requirements.txt
|
| 72 |
+
python app.py
|
| 73 |
+
```
|
| 74 |
+
Then open http://localhost:7860 and start labeling.
|
| 75 |
+
To add a password:
|
| 76 |
+
```bash
|
| 77 |
+
export AUTH_USER=myuser
|
| 78 |
+
export AUTH_PASS=mypassword
|
| 79 |
+
python app.py
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
---
|
| 83 |
+
|
| 84 |
+
## Tips for Medical Data
|
| 85 |
+
|
| 86 |
+
- If images are sensitive, keep the Space **private** (available on paid HF plans) or enable basic auth via `AUTH_USER`/`AUTH_PASS`.
|
| 87 |
+
- You can also host the same app on your institution's intranet server; it’s just a Gradio Python script.
|
| 88 |
+
|
| 89 |
+
---
|
| 90 |
+
|
| 91 |
+
## Troubleshooting
|
| 92 |
+
|
| 93 |
+
- **No images found**: Make sure your Space repo has an `/images` folder with JPG/PNG files.
|
| 94 |
+
- **CSV not updating in Git**: Set both `HF_TOKEN` and `HF_SPACE_REPO` secrets. The app uploads `labels.csv` using `huggingface_hub` on each write.
|
| 95 |
+
- **Multiple annotators**: Supported. A file lock prevents collisions when writing `labels.csv`.
|
app.py
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import os
|
| 3 |
+
import glob
|
| 4 |
+
import random
|
| 5 |
+
from datetime import datetime, timezone
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
|
| 8 |
+
import gradio as gr
|
| 9 |
+
import pandas as pd
|
| 10 |
+
from filelock import FileLock
|
| 11 |
+
from huggingface_hub import HfApi
|
| 12 |
+
|
| 13 |
+
# ---------------------- Configuration via ENV ----------------------
|
| 14 |
+
IMAGE_DIR = os.getenv("IMAGE_DIR", "images") # folder inside repo
|
| 15 |
+
LABELS_CSV = os.getenv("LABELS_CSV", "labels.csv")
|
| 16 |
+
LABEL_A = os.getenv("LABEL_A", "Class A")
|
| 17 |
+
LABEL_B = os.getenv("LABEL_B", "Class B")
|
| 18 |
+
APP_TITLE = os.getenv("APP_TITLE", "Two-Button Image Labeler")
|
| 19 |
+
APP_DESCRIPTION = os.getenv(
|
| 20 |
+
"APP_DESCRIPTION",
|
| 21 |
+
"Press the left or right button (or use keyboard shortcuts) to label each image. "
|
| 22 |
+
"Enter your name first so we can attribute labels."
|
| 23 |
+
)
|
| 24 |
+
SHOW_ALREADY_LABELED = os.getenv("SHOW_ALREADY_LABELED", "0") == "1" # if 0, hide images that already have a label
|
| 25 |
+
SHUFFLE_IMAGES = os.getenv("SHUFFLE_IMAGES", "1") == "1"
|
| 26 |
+
|
| 27 |
+
# Hugging Face repo auto-sync (optional)
|
| 28 |
+
HF_TOKEN = os.getenv("HF_TOKEN") # add as a secret in Spaces
|
| 29 |
+
HF_SPACE_REPO = os.getenv("HF_SPACE_REPO") # e.g. "username/space-name"
|
| 30 |
+
UPLOAD_TO_REPO = os.getenv("UPLOAD_TO_REPO", "1") == "1"
|
| 31 |
+
|
| 32 |
+
# Optional basic auth
|
| 33 |
+
AUTH_USER = os.getenv("AUTH_USER")
|
| 34 |
+
AUTH_PASS = os.getenv("AUTH_PASS")
|
| 35 |
+
|
| 36 |
+
SUPPORTED_EXTS = (".png", ".jpg", ".jpeg", ".bmp", ".gif", ".tif", ".tiff", ".webp")
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def list_images(image_dir: str):
|
| 40 |
+
paths = []
|
| 41 |
+
for p in glob.glob(os.path.join(image_dir, "**", "*"), recursive=True):
|
| 42 |
+
if p.lower().endswith(SUPPORTED_EXTS):
|
| 43 |
+
paths.append(p)
|
| 44 |
+
return sorted(paths)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def read_labels():
|
| 48 |
+
if os.path.exists(LABELS_CSV):
|
| 49 |
+
try:
|
| 50 |
+
return pd.read_csv(LABELS_CSV)
|
| 51 |
+
except Exception:
|
| 52 |
+
# if corrupted for any reason, don't crash
|
| 53 |
+
return pd.DataFrame(columns=["image", "label", "annotator", "timestamp"])
|
| 54 |
+
return pd.DataFrame(columns=["image", "label", "annotator", "timestamp"])
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def rel_to_image_dir(p: str):
|
| 58 |
+
# Make relative paths for portability
|
| 59 |
+
try:
|
| 60 |
+
return str(Path(p).resolve().relative_to(Path(IMAGE_DIR).resolve()))
|
| 61 |
+
except Exception:
|
| 62 |
+
return p
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def write_label(record: dict):
|
| 66 |
+
# Append a single record to LABELS_CSV safely
|
| 67 |
+
os.makedirs(os.path.dirname(LABELS_CSV) or ".", exist_ok=True)
|
| 68 |
+
with FileLock(LABELS_CSV + ".lock", timeout=10):
|
| 69 |
+
exists = os.path.exists(LABELS_CSV)
|
| 70 |
+
df = pd.DataFrame([record])
|
| 71 |
+
df.to_csv(LABELS_CSV, mode="a", header=not exists, index=False)
|
| 72 |
+
|
| 73 |
+
# Optional: upload the CSV to the Space repo so you automatically receive results
|
| 74 |
+
if HF_TOKEN and HF_SPACE_REPO and UPLOAD_TO_REPO:
|
| 75 |
+
try:
|
| 76 |
+
api = HfApi()
|
| 77 |
+
api.upload_file(
|
| 78 |
+
path_or_fileobj=LABELS_CSV,
|
| 79 |
+
path_in_repo=LABELS_CSV,
|
| 80 |
+
repo_id=HF_SPACE_REPO,
|
| 81 |
+
repo_type="space",
|
| 82 |
+
token=HF_TOKEN,
|
| 83 |
+
commit_message=f"Update labels: {datetime.now(timezone.utc).isoformat()}",
|
| 84 |
+
)
|
| 85 |
+
except Exception as e:
|
| 86 |
+
print("Warning: upload to repo failed:", e)
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def start_session(annotator):
|
| 90 |
+
annotator = (annotator or "").strip()
|
| 91 |
+
if not annotator:
|
| 92 |
+
raise gr.Error("Please enter your name before starting.")
|
| 93 |
+
|
| 94 |
+
imgs = list_images(IMAGE_DIR)
|
| 95 |
+
if not imgs:
|
| 96 |
+
raise gr.Error("No images found. Upload images to the 'images' folder.")
|
| 97 |
+
|
| 98 |
+
labels_df = read_labels()
|
| 99 |
+
if not SHOW_ALREADY_LABELED and len(labels_df) > 0:
|
| 100 |
+
labeled = set(labels_df["image"].astype(str).tolist())
|
| 101 |
+
# Images in CSV are stored as relative paths (if possible). Normalize current list to relative too.
|
| 102 |
+
rel_imgs = [rel_to_image_dir(p) for p in imgs]
|
| 103 |
+
imgs = [p for p, r in zip(imgs, rel_imgs) if r not in labeled]
|
| 104 |
+
|
| 105 |
+
if not imgs:
|
| 106 |
+
raise gr.Error("All images appear to be labeled already.")
|
| 107 |
+
|
| 108 |
+
if SHUFFLE_IMAGES:
|
| 109 |
+
random.shuffle(imgs)
|
| 110 |
+
|
| 111 |
+
state = {
|
| 112 |
+
"annotator": annotator,
|
| 113 |
+
"order": imgs,
|
| 114 |
+
"idx": 0,
|
| 115 |
+
"total": len(imgs),
|
| 116 |
+
}
|
| 117 |
+
progress = f"0 / {state['total']}"
|
| 118 |
+
# Show the first image
|
| 119 |
+
return state, imgs[0], progress, gr.update(visible=True), gr.update(visible=True), gr.update(visible=False)
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def next_image(state):
|
| 123 |
+
if state["idx"] >= state["total"]:
|
| 124 |
+
return None # triggers end
|
| 125 |
+
return state["order"][state["idx"]]
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def render_progress(state):
|
| 129 |
+
return f"{min(state['idx'], state['total'])} / {state['total']}"
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def label_and_next(state, choice):
|
| 133 |
+
if state is None:
|
| 134 |
+
raise gr.Error("Click 'Start' first.")
|
| 135 |
+
|
| 136 |
+
if state["idx"] >= state["total"]:
|
| 137 |
+
return state, None, "Done", gr.update(visible=False)
|
| 138 |
+
|
| 139 |
+
current_image = state["order"][state["idx"]]
|
| 140 |
+
record = {
|
| 141 |
+
"image": rel_to_image_dir(current_image),
|
| 142 |
+
"label": choice,
|
| 143 |
+
"annotator": state["annotator"],
|
| 144 |
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
| 145 |
+
}
|
| 146 |
+
write_label(record)
|
| 147 |
+
state["idx"] += 1
|
| 148 |
+
|
| 149 |
+
nxt = next_image(state)
|
| 150 |
+
if nxt is None:
|
| 151 |
+
# Finished
|
| 152 |
+
return state, None, "All done 🎉 Thank you!", gr.update(visible=False)
|
| 153 |
+
else:
|
| 154 |
+
return state, nxt, render_progress(state), gr.update(visible=True)
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def skip_and_next(state):
|
| 158 |
+
if state is None:
|
| 159 |
+
raise gr.Error("Click 'Start' first.")
|
| 160 |
+
state["idx"] += 1
|
| 161 |
+
nxt = next_image(state)
|
| 162 |
+
if nxt is None:
|
| 163 |
+
return state, None, "All done 🎉 Thank you!", gr.update(visible=False)
|
| 164 |
+
else:
|
| 165 |
+
return state, nxt, render_progress(state), gr.update(visible=True)
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def download_labels():
|
| 169 |
+
if not os.path.exists(LABELS_CSV):
|
| 170 |
+
raise gr.Error("No labels yet.")
|
| 171 |
+
return LABELS_CSV
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
with gr.Blocks(title=APP_TITLE, css=".gr-button {height: 64px; font-size: 18px;} .progress{font-weight:600}") as demo:
|
| 175 |
+
gr.Markdown(f"## {APP_TITLE}\n{APP_DESCRIPTION}")
|
| 176 |
+
|
| 177 |
+
with gr.Group():
|
| 178 |
+
with gr.Row():
|
| 179 |
+
annotator = gr.Textbox(label="Your name", placeholder="Type your name", scale=3)
|
| 180 |
+
start_btn = gr.Button("Start", variant="primary", scale=1)
|
| 181 |
+
with gr.Row():
|
| 182 |
+
info = gr.Markdown(
|
| 183 |
+
"1) Enter your name → 2) Click **Start** → 3) Use **Left / Right** buttons (shortcuts: **A** and **L**)."
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
image = gr.Image(label="Image", interactive=False)
|
| 187 |
+
progress = gr.Markdown("")
|
| 188 |
+
with gr.Row(visible=False) as buttons_row:
|
| 189 |
+
left = gr.Button(f"← {LABEL_A}", variant="secondary")
|
| 190 |
+
skip = gr.Button("Skip")
|
| 191 |
+
right = gr.Button(f"{LABEL_B} →", variant="secondary")
|
| 192 |
+
|
| 193 |
+
with gr.Accordion("Admin", open=False, visible=True) as admin:
|
| 194 |
+
with gr.Row():
|
| 195 |
+
download = gr.Button("Download CSV")
|
| 196 |
+
csv_file = gr.File(label="labels.csv", visible=False)
|
| 197 |
+
|
| 198 |
+
gr.Markdown(
|
| 199 |
+
"Tips: set `HF_TOKEN` and `HF_SPACE_REPO` secrets to automatically push `labels.csv` to the Space repository."
|
| 200 |
+
)
|
| 201 |
+
|
| 202 |
+
state = gr.State(None)
|
| 203 |
+
|
| 204 |
+
# Start flow
|
| 205 |
+
start_btn.click(
|
| 206 |
+
start_session,
|
| 207 |
+
inputs=[annotator],
|
| 208 |
+
outputs=[state, image, progress, buttons_row, admin, start_btn],
|
| 209 |
+
)
|
| 210 |
+
|
| 211 |
+
# Label buttons with shortcuts
|
| 212 |
+
left.click(
|
| 213 |
+
lambda s: label_and_next(s, LABEL_A),
|
| 214 |
+
inputs=[state],
|
| 215 |
+
outputs=[state, image, progress, buttons_row],
|
| 216 |
+
api_name="left_label",
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
+
right.click(
|
| 220 |
+
lambda s: label_and_next(s, LABEL_B),
|
| 221 |
+
inputs=[state],
|
| 222 |
+
outputs=[state, image, progress, buttons_row],
|
| 223 |
+
api_name="right_label",
|
| 224 |
+
)
|
| 225 |
+
|
| 226 |
+
skip.click(
|
| 227 |
+
skip_and_next,
|
| 228 |
+
inputs=[state],
|
| 229 |
+
outputs=[state, image, progress, buttons_row],
|
| 230 |
+
)
|
| 231 |
+
|
| 232 |
+
download.click(download_labels, outputs=[csv_file])
|
| 233 |
+
|
| 234 |
+
demo = demo.queue(max_size=64)
|
| 235 |
+
|
| 236 |
+
if __name__ == "__main__":
|
| 237 |
+
# Optional basic auth for local runs; on Spaces this will also work
|
| 238 |
+
if AUTH_USER and AUTH_PASS:
|
| 239 |
+
demo.launch(server_name="0.0.0.0", server_port=7860, auth=(AUTH_USER, AUTH_PASS))
|
| 240 |
+
else:
|
| 241 |
+
demo.launch(server_name="0.0.0.0", server_port=7860)
|
images/.gitkeep
ADDED
|
File without changes
|
images/BAS_0001.tiff
ADDED
|
|
Git LFS Details
|
images/KSC_0001.tiff
ADDED
|
|
Git LFS Details
|
images/LYT_0005.tiff
ADDED
|
|
Git LFS Details
|
images/MOB_0002.tiff
ADDED
|
|
Git LFS Details
|
images/MON_0002.tiff
ADDED
|
|
Git LFS Details
|
images/MYB_0003.tiff
ADDED
|
|
Git LFS Details
|
images/NGS_0002.tiff
ADDED
|
|
Git LFS Details
|
images/PMO_0002.tiff
ADDED
|
|
Git LFS Details
|
requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
gradio>=4.26.0
|
| 3 |
+
pandas>=2.1.0
|
| 4 |
+
huggingface_hub>=0.24.0
|
| 5 |
+
filelock>=3.13.0
|
| 6 |
+
Pillow>=10.0.0
|