Apiarist Dev commited on
Commit
686d2ed
Β·
1 Parent(s): 87f44ca

polish: seed sample hives, polished CSS theme, rich About + README, dataset live on Hub

Browse files
Files changed (3) hide show
  1. README.md +48 -8
  2. app.py +97 -11
  3. scripts/push_yolo_to_hub.py +179 -0
README.md CHANGED
@@ -7,19 +7,59 @@ sdk: gradio
7
  sdk_version: 6.16.0
8
  python_version: '3.12'
9
  app_file: app.py
10
- pinned: false
11
- license: mit
12
  short_description: Offline AI inspector for honeybee hive frames
 
 
 
 
 
 
 
 
 
 
 
13
  ---
14
 
15
  # 🐝 Apiarist
16
 
17
- Offline vision AI for backyard beekeepers. Point a phone at any honeycomb frame, get instant queen, varroa, and swarm-cell detection plus a weekly hive report. Runs fully local on a laptop β€” no cloud APIs, no API keys, no internet required.
 
18
 
19
- Built for the [Build Small Hackathon](https://huggingface.co/build-small-hackathon).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  ## Stack
22
- - **Vision-language model**: Qwen2.5-VL-7B (via llama.cpp)
23
- - **Specialist detector**: YOLOv8s fine-tuned on bee imagery
24
- - **UI**: Gradio with custom field-tool theme
25
- - **Storage**: SQLite, offline-first
 
 
 
 
 
 
 
 
 
 
 
7
  sdk_version: 6.16.0
8
  python_version: '3.12'
9
  app_file: app.py
10
+ pinned: true
11
+ license: apache-2.0
12
  short_description: Offline AI inspector for honeybee hive frames
13
+ tags:
14
+ - beekeeping
15
+ - object-detection
16
+ - vision-language-model
17
+ - small-models
18
+ - zerogpu
19
+ models:
20
+ - Qwen/Qwen2.5-VL-3B-Instruct
21
+ - maryammeda/apiarist-honey-bee-detector
22
+ datasets:
23
+ - maryammeda/apiarist-inaturalist-bees
24
  ---
25
 
26
  # 🐝 Apiarist
27
 
28
+ A fully-offline AI hive frame inspector for backyard beekeepers. Built
29
+ in 10 days for the [Build Small Hackathon](https://huggingface.co/build-small-hackathon).
30
 
31
+ ## What it does
32
+
33
+ Point a phone at any honeycomb frame, get back:
34
+
35
+ - bee / drone / queen / varroa-mite detections with bounding boxes
36
+ - a narrative inspection report
37
+ - auto-saved to a per-hive registry
38
+ - weekly PDF report on demand
39
+
40
+ All local. No cloud APIs at inference.
41
+
42
+ ## Architecture
43
+
44
+ | Layer | Model | Job |
45
+ |---|---|---|
46
+ | Specialist | [Custom YOLOv8s](https://huggingface.co/maryammeda/apiarist-honey-bee-detector) (22 MB) | Detects + counts bees, drones, queens, varroa mites |
47
+ | Generalist | [Qwen2.5-VL-3B-Instruct](https://huggingface.co/Qwen/Qwen2.5-VL-3B-Instruct) | Writes the narrative inspection report |
48
+ | Persistence | SQLite | Hive registry + inspection history |
49
+ | Reports | ReportLab | Weekly PDF generation |
50
 
51
  ## Stack
52
+
53
+ - πŸ€– [Hugging Face Spaces](https://huggingface.co/spaces) on ZeroGPU
54
+ - 🎨 [Gradio](https://gradio.app) with a custom field-tool theme
55
+ - ⚑ [Modal](https://modal.com) for fine-tuning the YOLO (1Γ— T4, 60 epochs)
56
+ - 🐝 Training data: [Hendricks Ricky bee-project](https://universe.roboflow.com/hendricks_ricky-hotmail-de/bee-project) (3,308 imgs, 892 queens)
57
+ - 🌿 Context imagery: [Apiarist iNaturalist bees dataset](https://huggingface.co/datasets/maryammeda/apiarist-inaturalist-bees)
58
+
59
+ ## Badges chased
60
+
61
+ πŸ”Œ Off the Grid Β· 🎯 Well-Tuned Β· 🎨 Off-Brand Β· πŸ“‘ Sharing is Caring Β· πŸ““ Field Notes
62
+
63
+ ## License
64
+
65
+ Apache 2.0
app.py CHANGED
@@ -410,23 +410,72 @@ def view_hive_history(hive_name):
410
 
411
  custom_css = """
412
  .gradio-container {
413
- background: #1a1410 !important;
414
  color: #f4e4bc !important;
415
- font-family: 'Courier New', monospace !important;
 
 
416
  }
417
- h1, h2, h3 { color: #f4a300 !important; }
418
- button.primary {
419
- background: #f4a300 !important;
 
 
 
420
  color: #1a1410 !important;
421
  font-weight: bold !important;
422
  border: none !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
423
  }
424
- .gr-box, .block { border-color: #f4a300 !important; }
425
  """
426
 
427
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
428
  def build_ui() -> gr.Blocks:
429
  db.init_db()
 
430
 
431
  with gr.Blocks(title="Apiarist - Hive Frame Inspector") as app:
432
  gr.Markdown("# 🐝 APIARIST")
@@ -557,13 +606,50 @@ def build_ui() -> gr.Blocks:
557
  with gr.Tab("ℹ️ About"):
558
  gr.Markdown(
559
  """
560
- **Apiarist** is a fully-offline vision AI for backyard beekeepers.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
561
 
562
- - πŸ”Œ No cloud APIs β€” runs entirely on the laptop
563
- - 🎯 Custom-trained on labeled bee imagery
564
- - πŸ““ Built in 10 days for the [Build Small Hackathon](https://huggingface.co/build-small-hackathon)
565
 
566
- **Stack**: Qwen2.5-VL-3B + custom YOLOv8s on ZeroGPU, SQLite persistence, Gradio UI.
567
  """
568
  )
569
 
 
410
 
411
  custom_css = """
412
  .gradio-container {
413
+ background: linear-gradient(180deg, #1a1410 0%, #2a1f15 100%) !important;
414
  color: #f4e4bc !important;
415
+ font-family: 'JetBrains Mono', 'Courier New', monospace !important;
416
+ max-width: 1400px !important;
417
+ margin: 0 auto !important;
418
  }
419
+ h1 { color: #f4a300 !important; letter-spacing: 0.05em; }
420
+ h2, h3 { color: #f4a300 !important; }
421
+ h4 { color: #ffd066 !important; }
422
+ button.primary,
423
+ button[variant="primary"] {
424
+ background: linear-gradient(180deg, #ffb733 0%, #f4a300 100%) !important;
425
  color: #1a1410 !important;
426
  font-weight: bold !important;
427
  border: none !important;
428
+ box-shadow: 0 2px 8px rgba(244, 163, 0, 0.25) !important;
429
+ transition: transform 80ms ease, box-shadow 200ms ease !important;
430
+ }
431
+ button.primary:hover {
432
+ transform: translateY(-1px);
433
+ box-shadow: 0 4px 14px rgba(244, 163, 0, 0.40) !important;
434
+ }
435
+ .gr-box, .block, .gradio-container .block {
436
+ border-color: rgba(244, 163, 0, 0.35) !important;
437
+ background: rgba(42, 31, 21, 0.4) !important;
438
+ }
439
+ .tabs > .tab-nav > button.selected {
440
+ color: #f4a300 !important;
441
+ border-bottom: 2px solid #f4a300 !important;
442
+ }
443
+ .gradio-container a {
444
+ color: #ffd066 !important;
445
+ }
446
+ .gradio-container a:hover {
447
+ color: #ffe599 !important;
448
+ text-decoration: underline;
449
+ }
450
+ table {
451
+ border-color: rgba(244, 163, 0, 0.25) !important;
452
  }
 
453
  """
454
 
455
 
456
+ def _seed_sample_data():
457
+ """Drop a handful of plausible hives into the DB on first boot
458
+ so the Hives tab isn't empty for first-visit judges."""
459
+ if db.list_hives():
460
+ return # someone already has data
461
+ samples = [
462
+ ("Hive #1 β€” Apricot tree", "South corner of yard", "yellow",
463
+ "Italian queen, marked yellow"),
464
+ ("Hive #2 β€” Cedar fence", "East side, near cedar", "red",
465
+ "2024 queen, watch for supersedure cells"),
466
+ ("Hive #3 β€” Vegetable garden", "By the tomato beds", "white",
467
+ "Newly split from Hive #1 in May"),
468
+ ]
469
+ for name, loc, marker, notes in samples:
470
+ try:
471
+ db.add_hive(name, loc, marker, notes)
472
+ except Exception:
473
+ pass
474
+
475
+
476
  def build_ui() -> gr.Blocks:
477
  db.init_db()
478
+ _seed_sample_data()
479
 
480
  with gr.Blocks(title="Apiarist - Hive Frame Inspector") as app:
481
  gr.Markdown("# 🐝 APIARIST")
 
606
  with gr.Tab("ℹ️ About"):
607
  gr.Markdown(
608
  """
609
+ ## 🐝 Apiarist
610
+
611
+ An offline AI hive frame inspector for backyard beekeepers. Built in
612
+ 10 days for the [Build Small Hackathon](https://huggingface.co/build-small-hackathon).
613
+
614
+ ### The problem
615
+
616
+ Frank β€” a beekeeper down the road β€” keeps 14 hives and inspects each
617
+ weekend by hand. Last summer he lost four colonies to queen failures
618
+ he didn't spot in time. Apiarist is the assistant he should have had:
619
+ fast, focused, runs on a laptop in a field, never sends his hive data
620
+ anywhere.
621
+
622
+ ### How it works
623
+
624
+ Two models, two jobs:
625
+
626
+ - **YOLOv8s, custom-trained** (22 MB) β€” finds bees, drones, queens, and
627
+ varroa mites with bounding boxes. CPU-fast.
628
+ - **Qwen2.5-VL-3B-Instruct** β€” takes the image + YOLO detection counts
629
+ and writes a narrative inspection report. Runs on ZeroGPU.
630
+
631
+ Together: structured detection + narrative report in ~5 seconds, no
632
+ internet required.
633
+
634
+ ### What's in the box
635
+
636
+ - πŸ” **Inspect** β€” upload a frame, get an annotated image + report
637
+ - πŸ“‹ **Hives** β€” registry, per-hive inspection history, weekly PDF reports
638
+ - βš–οΈ **Compare** β€” Apiarist vs raw generalist VLM, side-by-side
639
+ - πŸ““ SQLite persistence for the whole apiary
640
+
641
+ ### Stack
642
+
643
+ - Vision-language model: [Qwen2.5-VL-3B-Instruct](https://huggingface.co/Qwen/Qwen2.5-VL-3B-Instruct)
644
+ - Specialist detector: [Apiarist Honey-Bee Detector (custom YOLOv8s)](https://huggingface.co/maryammeda/apiarist-honey-bee-detector)
645
+ - Training data: [Hendricks Ricky bee-project](https://universe.roboflow.com/hendricks_ricky-hotmail-de/bee-project) on Roboflow Universe (3,308 labeled images, 892 queens)
646
+ - Bee imagery: [Apiarist iNaturalist bees dataset](https://huggingface.co/datasets/maryammeda/apiarist-inaturalist-bees)
647
+ - Compute: Modal (training), Hugging Face ZeroGPU (inference)
648
+ - Frontend: Gradio with custom field-tool theme
649
 
650
+ ### Badges chased
 
 
651
 
652
+ πŸ”Œ Off the Grid Β· 🎯 Well-Tuned Β· 🎨 Off-Brand Β· πŸ“‘ Sharing is Caring Β· πŸ““ Field Notes
653
  """
654
  )
655
 
scripts/push_yolo_to_hub.py ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Push the trained YOLOv8s honey-bee detector to a Hugging Face model repo.
3
+
4
+ Earns the "Sharing is Caring" badge. Re-run any time the local
5
+ weights/honey_bee_detector.pt is updated.
6
+
7
+ Usage:
8
+ py scripts/push_yolo_to_hub.py --repo-id maryammeda/apiarist-honey-bee-detector
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import argparse
14
+ import os
15
+ from pathlib import Path
16
+
17
+ from dotenv import load_dotenv
18
+ from huggingface_hub import HfApi
19
+
20
+
21
+ WEIGHTS_PATH = Path(__file__).parent.parent / "weights" / "honey_bee_detector.pt"
22
+
23
+
24
+ README_TEMPLATE = """---
25
+ license: apache-2.0
26
+ library_name: ultralytics
27
+ tags:
28
+ - yolo
29
+ - yolov8
30
+ - object-detection
31
+ - bees
32
+ - beekeeping
33
+ - computer-vision
34
+ pipeline_tag: object-detection
35
+ ---
36
+
37
+ # Apiarist Honey-Bee Detector (YOLOv8s)
38
+
39
+ A custom-trained **YOLOv8s** specialist detector for honeycomb frame
40
+ inspection. Built as part of [Apiarist](https://huggingface.co/spaces/build-small-hackathon/Apiarist) β€” a fully-offline AI hive inspector for
41
+ backyard beekeepers, made for the
42
+ [Build Small Hackathon](https://huggingface.co/build-small-hackathon).
43
+
44
+ ## Classes
45
+
46
+ Trained on the [hendricks_ricky bee-project](https://universe.roboflow.com/hendricks_ricky-hotmail-de/bee-project) dataset
47
+ (3,308 labeled images, including 892 queen-bee examples). Four classes:
48
+
49
+ - `Queen` β€” queen bee (larger, elongated abdomen)
50
+ - `Worker` β€” worker / forager bees (the majority class)
51
+ - `Drone` β€” male / drone bees
52
+ - `Varroa` β€” varroa destructor mites visible on bees or comb
53
+
54
+ ## Usage
55
+
56
+ ```python
57
+ from ultralytics import YOLO
58
+ from PIL import Image
59
+
60
+ model = YOLO("honey_bee_detector.pt")
61
+ results = model(Image.open("hive_frame.jpg"), conf=0.10, device="cpu")
62
+ for box in results[0].boxes:
63
+ cls = model.names[int(box.cls.item())]
64
+ conf = float(box.conf.item())
65
+ print(f"{cls}: {conf:.0%}")
66
+ ```
67
+
68
+ Recommended per-class confidence thresholds (tuned empirically on real
69
+ frame photos):
70
+
71
+ ```python
72
+ PER_CLASS_CONF = {
73
+ "Worker": 0.25,
74
+ "Drone": 0.55, # higher β€” drones false-positive on fingers, shadows
75
+ "Varroa": 0.30, # mites are small
76
+ "Queen": 0.15, # lower β€” try harder to find her
77
+ }
78
+ ```
79
+
80
+ ## Training
81
+
82
+ - Base weights: `yolov8s.pt`
83
+ - Epochs: 60
84
+ - Image size: 640Γ—640
85
+ - Batch size: 32
86
+ - Hardware: 1Γ— NVIDIA T4 (Modal)
87
+ - Cost: ~$0.40 of free hackathon credit
88
+
89
+ ## Limitations
90
+
91
+ The training set includes ~892 queen images, which is far more than the
92
+ typical bee-detection dataset but still limited compared to the
93
+ millions of bees in worker class. Queen detection precision is
94
+ moderate β€” when the model labels a queen, verify visually. Use as a
95
+ **candidate flag**, not as ground truth.
96
+
97
+ Varroa mite detection works on close-up frames where mites are visible
98
+ on the dorsal side of bees or in cells, but will miss mites hidden
99
+ under abdomens.
100
+
101
+ ## License
102
+
103
+ Apache 2.0. Trained on data released under CC BY 4.0 by the original
104
+ dataset authors.
105
+
106
+ ## Citation
107
+
108
+ If you use this model, please credit:
109
+ - This work: Apiarist (Build Small Hackathon entry, 2026)
110
+ - Training data: hendricks_ricky/bee-project, Roboflow Universe
111
+ """
112
+
113
+
114
+ def main() -> None:
115
+ parser = argparse.ArgumentParser()
116
+ parser.add_argument(
117
+ "--repo-id",
118
+ required=True,
119
+ help="Target HF repo, e.g. 'maryammeda/apiarist-honey-bee-detector'",
120
+ )
121
+ parser.add_argument(
122
+ "--private",
123
+ action="store_true",
124
+ help="Create repo as private (default public)",
125
+ )
126
+ args = parser.parse_args()
127
+
128
+ load_dotenv()
129
+ token = (
130
+ os.environ.get("HF_TOKEN")
131
+ or os.environ.get("HUGGING_FACE_HUB_TOKEN")
132
+ )
133
+ if not token:
134
+ raise SystemExit(
135
+ "Need HF_TOKEN in .env. Get one at "
136
+ "https://huggingface.co/settings/tokens (write scope)."
137
+ )
138
+
139
+ if not WEIGHTS_PATH.exists():
140
+ raise SystemExit(f"Weights not found at {WEIGHTS_PATH}")
141
+
142
+ print(f"Weights: {WEIGHTS_PATH} ({WEIGHTS_PATH.stat().st_size / 1024 / 1024:.1f} MB)")
143
+
144
+ api = HfApi(token=token)
145
+ print(f"Creating model repo {args.repo_id} ...")
146
+ api.create_repo(
147
+ repo_id=args.repo_id,
148
+ repo_type="model",
149
+ private=args.private,
150
+ exist_ok=True,
151
+ )
152
+
153
+ # Write a model card README
154
+ print("Uploading README ...")
155
+ api.upload_file(
156
+ path_or_fileobj=README_TEMPLATE.encode("utf-8"),
157
+ path_in_repo="README.md",
158
+ repo_id=args.repo_id,
159
+ repo_type="model",
160
+ commit_message="Add model card",
161
+ )
162
+
163
+ print(f"Uploading {WEIGHTS_PATH.name} ...")
164
+ api.upload_file(
165
+ path_or_fileobj=str(WEIGHTS_PATH),
166
+ path_in_repo="honey_bee_detector.pt",
167
+ repo_id=args.repo_id,
168
+ repo_type="model",
169
+ commit_message="Upload YOLOv8s honey-bee detector weights",
170
+ )
171
+
172
+ print(
173
+ f"\n[OK] Model live at: "
174
+ f"https://huggingface.co/{args.repo_id}"
175
+ )
176
+
177
+
178
+ if __name__ == "__main__":
179
+ main()