biodex 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
app.py ADDED
@@ -0,0 +1,344 @@
1
+ """
2
+ BioDex — Local AI for Wildlife Camera Traps (v1.0)
3
+
4
+ Gradio web UI with tabbed Dashboard, Batch, Video, Analytics, and Settings.
5
+ All inference runs locally.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import logging
11
+ import threading
12
+ from pathlib import Path
13
+ from typing import Any, cast
14
+
15
+ import gradio as gr
16
+ from core.config import get_settings
17
+ from core.detector import warmup_models
18
+ from core.types import BIODEX_VERSION
19
+ from ui.api_menu import build_api_menu
20
+ from ui.components import footer_chips_html, footer_tagline_html, header_html
21
+ from ui.handlers import (
22
+ ai_review_frame,
23
+ analyze_batch,
24
+ analyze_spot_check,
25
+ analyze_video_ui,
26
+ apply_settings,
27
+ clear_batch_review,
28
+ clear_llm_settings,
29
+ compute_analytics,
30
+ load_lila_cache,
31
+ on_llm_provider_change,
32
+ refresh_species_status,
33
+ request_cancel,
34
+ run_quick_demo,
35
+ save_llm_settings,
36
+ select_batch_frame,
37
+ test_llm_settings,
38
+ toggle_api_menu,
39
+ )
40
+ from ui.styles import APP_THEME, CUSTOM_CSS, tree_background_css
41
+ from ui.tabs import (
42
+ build_analytics_tab,
43
+ build_batch_tab,
44
+ build_dashboard_tab,
45
+ build_settings_tab,
46
+ build_video_tab,
47
+ )
48
+
49
+ LILA_CACHE_DIR = Path.home() / ".cache" / "biodex" / "channel-islands-demo"
50
+ FAVICON_PATH = Path(__file__).resolve().parent / "ui" / "favicon.png"
51
+ TREE_BACKGROUND_PATH = Path(__file__).resolve().parent / "ui" / "tree_of_life_background.jpg"
52
+ logger = logging.getLogger(__name__)
53
+
54
+
55
+ def build_app() -> gr.Blocks:
56
+ """Construct the tabbed BioDex Gradio application."""
57
+ settings = get_settings()
58
+ page_classes = ["biodex-page"]
59
+
60
+ with gr.Blocks(title=f"BioDex v{BIODEX_VERSION}") as demo:
61
+ review_state = gr.State([])
62
+ batch_paths_state = gr.State([])
63
+ last_batch = gr.State(None)
64
+
65
+ with gr.Column(elem_classes=[*page_classes, "field-device"]):
66
+ gr.HTML(header_html())
67
+
68
+ with gr.Tabs(elem_classes=["biodex-tabs"]):
69
+ build_dashboard_tab(last_batch)
70
+ batch_w = build_batch_tab(settings, review_state, batch_paths_state, last_batch)
71
+ video_w = build_video_tab()
72
+ analytics_w = build_analytics_tab(last_batch)
73
+ settings_w = build_settings_tab()
74
+
75
+ with gr.Column(elem_classes=["field-footer-section"]):
76
+ gr.HTML(footer_tagline_html())
77
+ api_open = gr.State(False)
78
+ api_w = build_api_menu()
79
+ with gr.Row(elem_classes=["field-footer-actions"]):
80
+ api_toggle_btn = gr.Button("Use via API", elem_classes=["field-api-toggle"])
81
+ gr.HTML(footer_chips_html())
82
+
83
+ api_toggle_btn.click(
84
+ fn=toggle_api_menu,
85
+ inputs=[api_open],
86
+ outputs=[
87
+ api_open,
88
+ api_w["api_menu"],
89
+ api_w["llm_provider"],
90
+ api_w["llm_api_key"],
91
+ api_w["llm_model"],
92
+ api_w["llm_base_url"],
93
+ api_w["llm_status"],
94
+ ],
95
+ )
96
+ api_w["llm_provider"].change(
97
+ fn=on_llm_provider_change,
98
+ inputs=[api_w["llm_provider"]],
99
+ outputs=[api_w["llm_model"], api_w["llm_base_url"], api_w["llm_status"]],
100
+ )
101
+ api_w["llm_save_btn"].click(
102
+ fn=save_llm_settings,
103
+ inputs=[
104
+ api_w["llm_provider"],
105
+ api_w["llm_api_key"],
106
+ api_w["llm_model"],
107
+ api_w["llm_base_url"],
108
+ ],
109
+ outputs=[api_w["llm_status"]],
110
+ )
111
+ api_w["llm_test_btn"].click(
112
+ fn=test_llm_settings,
113
+ inputs=[
114
+ api_w["llm_provider"],
115
+ api_w["llm_api_key"],
116
+ api_w["llm_model"],
117
+ api_w["llm_base_url"],
118
+ ],
119
+ outputs=[api_w["llm_status"]],
120
+ )
121
+ api_w["llm_clear_btn"].click(
122
+ fn=clear_llm_settings,
123
+ outputs=[
124
+ api_w["llm_provider"],
125
+ api_w["llm_api_key"],
126
+ api_w["llm_model"],
127
+ api_w["llm_base_url"],
128
+ api_w["llm_status"],
129
+ ],
130
+ )
131
+
132
+ # Batch tab wiring
133
+ batch_w["classify_species"].change(
134
+ fn=refresh_species_status,
135
+ inputs=[batch_w["classify_species"]],
136
+ outputs=[batch_w["species_status"]],
137
+ )
138
+ batch_w["load_cache_btn"].click(
139
+ fn=load_lila_cache,
140
+ outputs=[batch_w["batch_paths_state"], batch_w["batch_status"]],
141
+ )
142
+ batch_w["quick_demo_btn"].click(
143
+ fn=run_quick_demo,
144
+ inputs=[batch_w["batch_files"], batch_w["batch_paths_state"], batch_w["threshold"]],
145
+ outputs=[
146
+ batch_w["batch_stats"],
147
+ batch_w["batch_table"],
148
+ batch_w["batch_status"],
149
+ batch_w["species_status"],
150
+ batch_w["batch_csv_btn"],
151
+ batch_w["batch_json_btn"],
152
+ batch_w["batch_zip_btn"],
153
+ batch_w["batch_wi_btn"],
154
+ batch_w["batch_inat_btn"],
155
+ batch_w["batch_eco_btn"],
156
+ batch_w["review_state"],
157
+ batch_w["review_original"],
158
+ batch_w["review_annotated"],
159
+ batch_w["frame_label"],
160
+ batch_w["frame_detections"],
161
+ batch_w["review_panel"],
162
+ last_batch,
163
+ ],
164
+ show_progress="minimal",
165
+ )
166
+ batch_w["batch_btn"].click(
167
+ fn=analyze_batch,
168
+ inputs=[
169
+ batch_w["batch_files"],
170
+ batch_w["batch_paths_state"],
171
+ batch_w["threshold"],
172
+ batch_w["classify_species"],
173
+ ],
174
+ outputs=[
175
+ batch_w["batch_stats"],
176
+ batch_w["batch_table"],
177
+ batch_w["batch_status"],
178
+ batch_w["species_status"],
179
+ batch_w["batch_csv_btn"],
180
+ batch_w["batch_json_btn"],
181
+ batch_w["batch_zip_btn"],
182
+ batch_w["batch_wi_btn"],
183
+ batch_w["batch_inat_btn"],
184
+ batch_w["batch_eco_btn"],
185
+ batch_w["review_state"],
186
+ batch_w["review_original"],
187
+ batch_w["review_annotated"],
188
+ batch_w["frame_label"],
189
+ batch_w["frame_detections"],
190
+ batch_w["review_panel"],
191
+ last_batch,
192
+ ],
193
+ show_progress="minimal",
194
+ )
195
+ for _reset_btn in (batch_w["batch_btn"], batch_w["quick_demo_btn"], batch_w["clear_btn"]):
196
+ _reset_btn.click(
197
+ fn=lambda: (None, ""),
198
+ outputs=[batch_w["selected_frame_index"], batch_w["ai_review_output"]],
199
+ )
200
+ batch_w["cancel_btn"].click(fn=request_cancel, outputs=[batch_w["batch_status"]])
201
+ batch_w["batch_table"].select(
202
+ fn=select_batch_frame,
203
+ inputs=[batch_w["review_state"]],
204
+ outputs=[
205
+ batch_w["review_original"],
206
+ batch_w["review_annotated"],
207
+ batch_w["frame_label"],
208
+ batch_w["frame_detections"],
209
+ batch_w["selected_frame_index"],
210
+ batch_w["ai_review_output"],
211
+ ],
212
+ )
213
+ batch_w["ai_review_btn"].click(
214
+ fn=ai_review_frame,
215
+ inputs=[batch_w["review_state"], batch_w["selected_frame_index"]],
216
+ outputs=[batch_w["ai_review_output"]],
217
+ show_progress="minimal",
218
+ )
219
+ batch_w["clear_btn"].click(
220
+ fn=clear_batch_review,
221
+ outputs=[
222
+ batch_w["batch_stats"],
223
+ batch_w["batch_table"],
224
+ batch_w["batch_status"],
225
+ batch_w["species_status"],
226
+ batch_w["batch_csv_btn"],
227
+ batch_w["batch_json_btn"],
228
+ batch_w["batch_zip_btn"],
229
+ batch_w["batch_wi_btn"],
230
+ batch_w["batch_inat_btn"],
231
+ batch_w["batch_eco_btn"],
232
+ batch_w["review_state"],
233
+ batch_w["review_original"],
234
+ batch_w["review_annotated"],
235
+ batch_w["frame_label"],
236
+ batch_w["frame_detections"],
237
+ batch_w["review_panel"],
238
+ batch_w["batch_paths_state"],
239
+ last_batch,
240
+ ],
241
+ )
242
+ batch_w["analyze_one_btn"].click(
243
+ fn=analyze_spot_check,
244
+ inputs=[batch_w["input_image"], batch_w["threshold"], batch_w["classify_species"]],
245
+ outputs=[
246
+ batch_w["spot_original"],
247
+ batch_w["spot_annotated"],
248
+ batch_w["spot_stats"],
249
+ batch_w["spot_table"],
250
+ batch_w["batch_status"],
251
+ ],
252
+ show_progress="minimal",
253
+ )
254
+
255
+ # Video tab
256
+ video_w["video_analyze_btn"].click(
257
+ fn=analyze_video_ui,
258
+ inputs=[
259
+ video_w["video_file"],
260
+ video_w["video_threshold"],
261
+ video_w["video_classify"],
262
+ video_w["video_fps"],
263
+ video_w["video_max_frames"],
264
+ ],
265
+ outputs=[
266
+ video_w["video_status"],
267
+ video_w["video_timeline_btn"],
268
+ video_w["video_gallery"],
269
+ video_w["video_results_panel"],
270
+ ],
271
+ show_progress="minimal",
272
+ )
273
+ video_w["video_cancel_btn"].click(fn=request_cancel, outputs=[video_w["video_status"]])
274
+
275
+ # Analytics tab
276
+ analytics_w["analytics_refresh"].click(
277
+ fn=compute_analytics,
278
+ inputs=[last_batch],
279
+ outputs=[
280
+ analytics_w["diversity_html"],
281
+ analytics_w["heatmap_image"],
282
+ analytics_w["species_chart"],
283
+ analytics_w["analytics_results_panel"],
284
+ ],
285
+ )
286
+
287
+ # Settings tab
288
+ settings_w["settings_save"].click(
289
+ fn=apply_settings,
290
+ inputs=[
291
+ settings_w["settings_threshold"],
292
+ settings_w["settings_geofence"],
293
+ ],
294
+ outputs=[
295
+ batch_w["threshold"],
296
+ settings_w["settings_status"],
297
+ ],
298
+ )
299
+
300
+ return cast(gr.Blocks, demo)
301
+
302
+
303
+ def _start_model_warmup(*, species: bool = False) -> None:
304
+ """Load MegaDetector in the background so Process Folder starts faster."""
305
+
306
+ def _run() -> None:
307
+ try:
308
+ warmup_models(species=species)
309
+ logger.info("Model warmup complete (species=%s).", species)
310
+ except Exception as exc:
311
+ logger.warning("Model warmup failed: %s", exc)
312
+
313
+ threading.Thread(target=_run, daemon=True, name="biodex-warmup").start()
314
+
315
+
316
+ def launch_app() -> None:
317
+ """Build and launch the Gradio UI (console entry: ``biodex-ui``)."""
318
+ settings = get_settings()
319
+ host = settings.host
320
+ port = settings.port
321
+ css = CUSTOM_CSS + tree_background_css()
322
+ print(f"BioDex v{BIODEX_VERSION} at http://{host}:{port}")
323
+ print("Open the Batch tab to process a folder, or try Quick demo.")
324
+ _start_model_warmup(species=True)
325
+ app = build_app()
326
+ if settings.enable_queue:
327
+ app.queue(default_concurrency_limit=2)
328
+ biodex_cache = Path.home() / ".cache" / "biodex"
329
+ launch_kwargs: dict[str, Any] = {
330
+ "server_name": host,
331
+ "server_port": port,
332
+ "theme": APP_THEME,
333
+ "css": css,
334
+ "auth": settings.gradio_auth,
335
+ "show_error": True,
336
+ "allowed_paths": [str(biodex_cache), str(LILA_CACHE_DIR), str(TREE_BACKGROUND_PATH.parent)],
337
+ }
338
+ if FAVICON_PATH.is_file():
339
+ launch_kwargs["favicon_path"] = str(FAVICON_PATH)
340
+ app.launch(**launch_kwargs)
341
+
342
+
343
+ if __name__ == "__main__":
344
+ launch_app()
biodex/__init__.py ADDED
@@ -0,0 +1,14 @@
1
+ """BioDex top-level package shim.
2
+
3
+ The implementation lives in the ``core``, ``ui``, and ``desktop`` packages.
4
+ This thin namespace re-exports the stable public surface and the version so
5
+ that ``import biodex`` and ``python -m biodex`` behave as users expect.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from core.cli import main
11
+ from core.types import BIODEX_VERSION
12
+
13
+ __version__ = BIODEX_VERSION
14
+ __all__ = ["main", "__version__"]
biodex/__main__.py ADDED
@@ -0,0 +1,8 @@
1
+ """Enable ``python -m biodex`` to invoke the BioDex CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from core.cli import main
6
+
7
+ if __name__ == "__main__":
8
+ main()
@@ -0,0 +1,181 @@
1
+ Metadata-Version: 2.4
2
+ Name: biodex
3
+ Version: 1.0.1
4
+ Summary: Local, privacy-first AI for wildlife camera trap analysis and conservation monitoring
5
+ Author: FratresMedAI
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: biodiversity,camera-trap,conservation,defensive-monitoring,megadetector,speciesnet,wildlife,wildlife-insights
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Classifier: Topic :: Scientific/Engineering :: Image Recognition
20
+ Requires-Python: <3.13,>=3.10
21
+ Requires-Dist: numpy<2.0,>=1.26.4
22
+ Requires-Dist: pandas>=2.1
23
+ Requires-Dist: pillow>=9.5
24
+ Requires-Dist: setuptools<81.0,>=65.0
25
+ Requires-Dist: tqdm>=4.64
26
+ Requires-Dist: typer>=0.12
27
+ Provides-Extra: all
28
+ Requires-Dist: gradio<7.0,>=5.0; extra == 'all'
29
+ Requires-Dist: matplotlib>=3.7; extra == 'all'
30
+ Requires-Dist: megadetector<11.0,>=10.0; extra == 'all'
31
+ Requires-Dist: mypy>=1.10; extra == 'all'
32
+ Requires-Dist: onnxruntime>=1.16; extra == 'all'
33
+ Requires-Dist: opencv-python-headless>=4.8; extra == 'all'
34
+ Requires-Dist: pyinstaller>=6.0; extra == 'all'
35
+ Requires-Dist: pytest>=8.0; extra == 'all'
36
+ Requires-Dist: ruff>=0.4; extra == 'all'
37
+ Requires-Dist: seaborn>=0.13; extra == 'all'
38
+ Requires-Dist: speciesnet<6.0,>=5.0; extra == 'all'
39
+ Requires-Dist: torch>=2.0; extra == 'all'
40
+ Requires-Dist: types-pillow; extra == 'all'
41
+ Provides-Extra: analytics
42
+ Requires-Dist: matplotlib>=3.7; extra == 'analytics'
43
+ Requires-Dist: seaborn>=0.13; extra == 'analytics'
44
+ Provides-Extra: desktop
45
+ Requires-Dist: pyinstaller>=6.0; extra == 'desktop'
46
+ Provides-Extra: dev
47
+ Requires-Dist: mypy>=1.10; extra == 'dev'
48
+ Requires-Dist: pytest>=8.0; extra == 'dev'
49
+ Requires-Dist: ruff>=0.4; extra == 'dev'
50
+ Requires-Dist: types-pillow; extra == 'dev'
51
+ Provides-Extra: edge
52
+ Requires-Dist: onnxruntime>=1.16; (platform_system != 'Darwin' or platform_machine != 'arm64') and extra == 'edge'
53
+ Provides-Extra: models
54
+ Requires-Dist: megadetector<11.0,>=10.0; extra == 'models'
55
+ Requires-Dist: speciesnet<6.0,>=5.0; extra == 'models'
56
+ Requires-Dist: torch>=2.0; extra == 'models'
57
+ Provides-Extra: ui
58
+ Requires-Dist: gradio<7.0,>=5.0; extra == 'ui'
59
+ Provides-Extra: video
60
+ Requires-Dist: opencv-python-headless>=4.8; extra == 'video'
61
+ Description-Content-Type: text/markdown
62
+
63
+ # BioDex
64
+
65
+ **Local AI for wildlife camera traps.** Detect animals, filter blanks, identify species, export results — on your machine, not in the cloud.
66
+
67
+ [![Python 3.10–3.12](https://img.shields.io/badge/python-3.10--3.12-blue.svg)](https://www.python.org/)
68
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
69
+ [![Version](https://img.shields.io/badge/version-1.0.1-brightgreen.svg)](CHANGELOG.md)
70
+ [![CI](https://github.com/FratresMedAI/BioDex/actions/workflows/ci.yml/badge.svg)](https://github.com/FratresMedAI/BioDex/actions/workflows/ci.yml)
71
+ [![Release](https://github.com/FratresMedAI/BioDex/actions/workflows/release.yml/badge.svg)](https://github.com/FratresMedAI/BioDex/actions/workflows/release.yml)
72
+
73
+ Built for conservation research, field review, and defensive wildlife monitoring (Fratres / EcoSentinel integration hooks).
74
+
75
+ ---
76
+
77
+ ## Run locally (do this)
78
+
79
+ **Mac / Linux**
80
+
81
+ ```bash
82
+ git clone https://github.com/FratresMedAI/BioDex.git
83
+ cd BioDex
84
+ ./run_biodex.sh
85
+ ```
86
+
87
+ **Windows**
88
+
89
+ ```bat
90
+ git clone https://github.com/FratresMedAI/BioDex.git
91
+ cd BioDex
92
+ run_biodex.bat
93
+ ```
94
+
95
+ Your browser opens **http://127.0.0.1:7860**. Use the **Batch** tab to process a folder, or **Quick demo** for a fast preview.
96
+
97
+ First analysis downloads models once (~500 MB). After that, everything stays offline on your computer.
98
+
99
+ ---
100
+
101
+ ## v1.0 highlights
102
+
103
+ - **Pluggable models** — registry architecture (`core/models/`) with MegaDetector + SpeciesNet adapters
104
+ - **Batch performance** — chunking, cancel, ETA progress, optional `torch.compile`
105
+ - **Video foundations** — frame sampling + timeline export (`biodex video`, requires `[video]` extra)
106
+ - **Advanced exports** — Wildlife Insights, iNaturalist drafts, timelapse JSON, SQLite, EcoSentinel hook
107
+ - **Tabbed UI** — Dashboard, Batch, Video, Analytics, Settings
108
+ - **Optional AI review (BYOK)** — per-frame LLM notes after batch runs (see below)
109
+ - **Docker** — CPU and GPU images for deployment
110
+ - **Release maturity** — stable API surface, strict typing/linting, and CI-gated quality
111
+
112
+ ---
113
+
114
+ ## Optional AI review (BYOK)
115
+
116
+ Core detection runs **fully offline** on your machine. AI review is an **optional** power feature:
117
+
118
+ 1. Open **Use via API** in the footer.
119
+ 2. Choose a provider, paste your API key, pick a model (or type a custom model ID), then **Save**.
120
+ 3. After a batch run, select a frame and click **AI review (LLM)** for a field note: scene summary, species second opinion, and data-quality flags.
121
+
122
+ **Privacy:** API keys are stored locally in `~/.cache/biodex/settings.json` and sent only to the provider you choose — never to BioDex servers. See [SECURITY.md](SECURITY.md).
123
+
124
+ **Scope in v1.0.1:** batch frame review only. Single-image spot check, video key frames, and batch-level summaries are planned for v1.1. Not every model slug in the dropdown is guaranteed to work with every provider — use a custom model ID if needed.
125
+
126
+ ---
127
+
128
+ ## Extras install matrix
129
+
130
+ ```bash
131
+ pip install -e ".[ui,models]" # Web UI + inference (default)
132
+ pip install -e ".[video]" # OpenCV video support
133
+ pip install -e ".[analytics]" # Heatmaps + diversity metrics
134
+ pip install -e ".[edge]" # ONNX stubs (future edge deploy)
135
+ pip install -e ".[all]" # Everything
136
+ ```
137
+
138
+ ---
139
+
140
+ ## Batch CLI
141
+
142
+ For large folders (100+ images), no browser:
143
+
144
+ ```bash
145
+ biodex batch /path/to/images -o ./results --classify-species --recursive
146
+ biodex batch /path/to/images -o ./results --chunk-size 500 --torch-compile
147
+ biodex video /path/to/clip.mp4 -o ./video-results --fps 1 --max-frames 120
148
+ ```
149
+
150
+ Environment variables: `BIODEX_DETECTOR_MODEL`, `BIODEX_TORCH_COMPILE`, `BIODEX_GEOFENCE_REGION`, `BIODEX_AUDIT_LOG=1`
151
+
152
+ ## Docker quick start
153
+
154
+ ```bash
155
+ docker build -t biodex:cpu -f Dockerfile .
156
+ docker run --rm -p 7860:7860 biodex:cpu
157
+ ```
158
+
159
+ GPU:
160
+
161
+ ```bash
162
+ docker build -t biodex:gpu -f Dockerfile.gpu .
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Developers
168
+
169
+ ```bash
170
+ pip install -e ".[ui,models,dev]"
171
+ pytest tests/ -v -m "not slow"
172
+ ruff check core app.py ui
173
+ mypy core app.py ui
174
+ pre-commit install # optional
175
+ ```
176
+
177
+ See [CHANGELOG.md](CHANGELOG.md), [docs/roadmap.md](docs/roadmap.md), [CONTRIBUTING.md](CONTRIBUTING.md), and [SECURITY.md](SECURITY.md).
178
+
179
+ ---
180
+
181
+ MIT License. Uses [MegaDetector](https://github.com/agentmorris/MegaDetector) and [SpeciesNet](https://github.com/google/cameratrapai).
@@ -0,0 +1,46 @@
1
+ biodex/__init__.py,sha256=yZhDYkaRQ0IibctDfn1Z8wQijw6iYiFqYka_1O40JQM,428
2
+ biodex/__main__.py,sha256=-QUOWVDd3uyUY7OaH55Te9gqRBtFCtAu1CbfKrSAtsY,162
3
+ core/__init__.py,sha256=LymJRor95Bw_GZfnBmP7xACxc25zsy7SydfX9As8sR0,1298
4
+ core/analytics.py,sha256=FfaqQYPePL665lC99YBt3v0U-QiVjP8bp6VajnpX6EQ,4001
5
+ core/audit_log.py,sha256=pbpWVc8ORzw7vkA1zUMNWCxjTluf1y4oSkfoAEYijGE,1851
6
+ core/batch.py,sha256=JuAjUUrUu1-NJN8poM7TUvl8Kb_iyZr7wyR-P3avyiw,8548
7
+ core/batch_report.py,sha256=Dx9UaAT2LmrQeVOF-YDyZjZ0DkIVSUPNgS6krSSo9jo,2539
8
+ core/classifier.py,sha256=G8QWDTlRN_KRyram4No8aqI7dxKEo8a_773ACX1OOTk,2111
9
+ core/cli.py,sha256=tzx4GKBZtoGry5pThxcwraPDWagdWPTvmt4ndhSzlgw,15892
10
+ core/config.py,sha256=qU2RGZ_5UaASou4mVJqtsddm-YbnWmJYDsFhY6w-wg0,2994
11
+ core/detector.py,sha256=zh9uQY-M_CE6n_Ve5WaZnew2dfGtmAnHh0_n7bvEYiE,9417
12
+ core/exif_utils.py,sha256=7Vh6_-QmO5BWhmH0p7AGffrBS9Nq-wTxsuYZeu2zz9E,3586
13
+ core/exports.py,sha256=mIhtSm1TbUFuaO8BydZrNdES5kHf23B23GuH0pWxLkA,19865
14
+ core/progress.py,sha256=VntwzW-On8Ae90UQJnaeXRWaCRdSmKzNrTUDuMTOZew,2532
15
+ core/quick_demo.py,sha256=OI4vg-ZAXKANi7XtPk1zj7sgbBRvdR8gKavQAmThTx4,4238
16
+ core/types.py,sha256=SMhYjLDMXPBG-zKykSO9jbvVEPsrC9GhHYtH9PYYh6I,7321
17
+ core/ui_entry.py,sha256=Pm9MMbN3f5jWo4cCCsLwLmprRQRwBChB3mJqhruRvLo,264
18
+ core/video.py,sha256=uXPmaQc1AGVO87prlmBui2RD-qdbWU9THNGU8jZHh8E,6913
19
+ core/visualization.py,sha256=sUqjv0KJh0IbivNzSdCGWstYIjRVVEq8JBeg8_BrbLI,11566
20
+ core/models/__init__.py,sha256=mMsnqfuPlsZiepmo3gEAXeFH1_zDrsy42MsBKEULvvk,1061
21
+ core/models/base.py,sha256=LZpxN1j-ZymWrSZTbxQpiLEmhPWzEix6Aoefo693u2w,1829
22
+ core/models/edge.py,sha256=xpIGzWUVuP1h69yyljCzflhhCiQNT1rQFB5pTq-UmAY,2232
23
+ core/models/megadetector.py,sha256=3-i3vIvn2xfVGnAyhDYfrljzB94EnJUXMpbDz3o0epY,5138
24
+ core/models/registry.py,sha256=fsL_JPOklVFpzUBz_hhIERdHj7Xjc_DvF9QXWF-MS_4,3876
25
+ core/models/speciesnet.py,sha256=8SLK-HdayoTu7Ik0dhS3zsFFHNlMTy2AWOBkb4wTn0U,8485
26
+ desktop/README.md,sha256=SLB_HIw6xkOXxJmY_I13EdxS4Rw0Yzlvz-zm855ovC4,370
27
+ desktop/__init__.py,sha256=wcZGxqfxuRPXS0ggC59UwivykN0Uu7g-ReaUa5QpePk,43
28
+ desktop/launcher.py,sha256=5QE1FztcQhGtYoV23g5qP0JV0DcDKxCSRicYhOwGjvM,3527
29
+ ui/__init__.py,sha256=fCBSWPVAFRPivSQtPvTele7ZNJMSPDu8Mz0P7Iq4F9Y,32
30
+ ui/api_menu.py,sha256=fwHcq0dRVt1kLRmS7gFCwtb1yXGF6L8AIC4glaINF8g,2678
31
+ ui/components.py,sha256=-bToo0QNul4O7KrCYrtJGoea8Gwc-cjwgvut5SnIdfw,18778
32
+ ui/favicon.png,sha256=jmrkHUTToBEHGZowNX1U2tuULXxPqu13OkRVuZF6-SU,126563
33
+ ui/handlers.py,sha256=SPp5r_7i4WnRVgQh6tHGVNIOuDd8sv8-f1Pk5_wgAqc,24405
34
+ ui/llm_review.py,sha256=xa3bdnU1tx9eoo9DzennbHO6Z2HFR2GabQEDxfXuCks,5585
35
+ ui/llm_settings.py,sha256=5pkJawTgTltZHN1F2pJvEF-ZlEh0vdMsmOpOo5Bv9aU,17839
36
+ ui/settings_store.py,sha256=12UF8juPQNE_Peg_mdCbfE9yYn6c43UUOFKbeIIQWi0,1356
37
+ ui/styles.py,sha256=UluaVxD7eq_3EfkmB8qcm5fVo1hn4HYo5tVq50RkyK8,36895
38
+ ui/tabs.py,sha256=e48IUIxWzXViu0zsJ9uWvnXv7v8PljLHqoho0U6UO1Q,9318
39
+ ui/tree_of_life_background.avif,sha256=eahqA_GlTN11vqt64h6-za3lo60OmHoB2lfixiye8vU,667171
40
+ ui/tree_of_life_background.jpg,sha256=ButfoDF5PPmClSNz6w3xYaGyQB47-Uo7ekV7Fj_JvIM,103634
41
+ app.py,sha256=ULI5rF0c7AQHpYs3Dk_2FG5G2TNs_aBofGAgFpEEKCw,11928
42
+ biodex-1.0.1.dist-info/METADATA,sha256=auIH0eCgQrwjJdH_ZXIEJirGjQ-IRxlL1SwlzmiohXI,7006
43
+ biodex-1.0.1.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
44
+ biodex-1.0.1.dist-info/entry_points.txt,sha256=o17nKvCdhiMUV3nQgjw4m02mVN31pIS_wn32BDVqZw0,111
45
+ biodex-1.0.1.dist-info/licenses/LICENSE,sha256=IbHSMRrvJuGumMZjY3m6GVnxbrGFlYr1Qp_UqPHb6U0,1076
46
+ biodex-1.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ biodex = core.cli:main
3
+ biodex-desktop = desktop.launcher:main
4
+ biodex-ui = core.ui_entry:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 BioDex Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
core/__init__.py ADDED
@@ -0,0 +1,55 @@
1
+ """BioDex core modules for detection, classification, visualization, and export."""
2
+
3
+ import core.models # noqa: F401 — register default detector/classifier adapters
4
+ from core.batch import run_batch
5
+ from core.classifier import DEFAULT_SPECIES_MIN_CONFIDENCE, enrich_with_species, get_classifier
6
+ from core.detector import (
7
+ CATEGORY_MAP,
8
+ MODEL_ID,
9
+ analyze_single_image,
10
+ get_category_label,
11
+ get_detector,
12
+ run_analysis,
13
+ run_detection,
14
+ )
15
+ from core.exports import (
16
+ batch_to_csv,
17
+ detections_to_csv,
18
+ export_batch_json,
19
+ export_bundle,
20
+ export_json,
21
+ save_annotated_image,
22
+ )
23
+ from core.types import (
24
+ BIODEX_VERSION,
25
+ AnalysisResult,
26
+ BatchResult,
27
+ DetectionRecord,
28
+ SpeciesPrediction,
29
+ )
30
+ from core.visualization import draw_detections
31
+
32
+ __all__ = [
33
+ "BIODEX_VERSION",
34
+ "CATEGORY_MAP",
35
+ "MODEL_ID",
36
+ "AnalysisResult",
37
+ "BatchResult",
38
+ "DetectionRecord",
39
+ "SpeciesPrediction",
40
+ "analyze_single_image",
41
+ "batch_to_csv",
42
+ "draw_detections",
43
+ "detections_to_csv",
44
+ "enrich_with_species",
45
+ "export_batch_json",
46
+ "export_bundle",
47
+ "export_json",
48
+ "get_category_label",
49
+ "get_classifier",
50
+ "get_detector",
51
+ "run_analysis",
52
+ "run_batch",
53
+ "run_detection",
54
+ "save_annotated_image",
55
+ ]