Spaces:
Runtime error
Runtime error
docs: elimina docs _mvp y _modelo_conteo_plantas (a pedido) + corrige enlaces
Browse files- Borra architecture_agrovision_mvp.md, architecture_modelo_conteo_plantas.md,
description_proyecto_agrovision_mvp.md, description_proyecto_modelo_conteo_plantas.md.
- Relativiza/quita los enlaces a esos archivos en README, description/architecture/db/ejecucion
(el modelo de conteo vive en un repo separado).
- README.md +1 -1
- docs/architect/architecture_agrovision.md +1 -1
- docs/architect/architecture_agrovision_mvp.md +0 -158
- docs/architect/architecture_modelo_conteo_plantas.md +0 -192
- docs/db/diseno_db.md +1 -1
- docs/ejecucion.md +1 -1
- docs/reference/description_proyecto_agrovision.md +3 -4
- docs/reference/description_proyecto_agrovision_mvp.md +0 -237
- docs/reference/description_proyecto_modelo_conteo_plantas.md +0 -297
README.md
CHANGED
|
@@ -4,7 +4,7 @@ Plataforma de [AgroVisión](docs/reference/description_proyecto_agrovision.md) p
|
|
| 4 |
|
| 5 |
> **Modelo BYOK, credenciales efímeras.** Las llaves del usuario (Supabase, Copernicus, Groq) viven **solo en memoria de sesión** y se envían por cabeceras `X-User-*`; nunca se persisten. Refrescar borra todo.
|
| 6 |
>
|
| 7 |
-
> **Conteo por dron — EN DESARROLLO.** Arranca deshabilitado (`COUNTING_ENABLED=false`); la cola/worker/tabla existen pero inactivos hasta que el
|
| 8 |
|
| 9 |
## Módulos (6)
|
| 10 |
|
|
|
|
| 4 |
|
| 5 |
> **Modelo BYOK, credenciales efímeras.** Las llaves del usuario (Supabase, Copernicus, Groq) viven **solo en memoria de sesión** y se envían por cabeceras `X-User-*`; nunca se persisten. Refrescar borra todo.
|
| 6 |
>
|
| 7 |
+
> **Conteo por dron — EN DESARROLLO.** Arranca deshabilitado (`COUNTING_ENABLED=false`); la cola/worker/tabla existen pero inactivos hasta que el **repo del modelo** (proyecto separado) publique el artefacto `agrovision-plantcount` en Hugging Face Hub. **Licencia: AGPL-3.0.**
|
| 8 |
|
| 9 |
## Módulos (6)
|
| 10 |
|
docs/architect/architecture_agrovision.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
# Arquitectura — AgroVisión (Plataforma Completa)
|
| 2 |
|
| 3 |
> **Audiencia:** Arquitectos de solución, líderes técnicos, desarrolladores.
|
| 4 |
-
> **Alcance:** Estructura fundamental del sistema, interacciones de alto nivel (C4), esquema de datos y modelo de despliegue de la **plataforma completa** (**6 módulos**). Para especificaciones funcionales, ver [`description_proyecto_agrovision.md`](../reference/description_proyecto_agrovision.md).
|
| 5 |
>
|
| 6 |
> **Estilo arquitectónico:** **monolito modular** (un solo backend FastAPI con límites por dominio bien aislados — `api/` HTTP + `services/` negocio), **microservices-ready**: cada dominio puede extraerse a un servicio propio sin reescribir la UI. Elegido sobre microservicios reales por la realidad de la **capa gratuita** (Render free duerme a 15 min y multiplica cold-starts/CORS por servicio). Ver ADR en §7.
|
| 7 |
>
|
|
|
|
| 1 |
# Arquitectura — AgroVisión (Plataforma Completa)
|
| 2 |
|
| 3 |
> **Audiencia:** Arquitectos de solución, líderes técnicos, desarrolladores.
|
| 4 |
+
> **Alcance:** Estructura fundamental del sistema, interacciones de alto nivel (C4), esquema de datos y modelo de despliegue de la **plataforma completa** (**6 módulos**). Para especificaciones funcionales, ver [`description_proyecto_agrovision.md`](../reference/description_proyecto_agrovision.md).
|
| 5 |
>
|
| 6 |
> **Estilo arquitectónico:** **monolito modular** (un solo backend FastAPI con límites por dominio bien aislados — `api/` HTTP + `services/` negocio), **microservices-ready**: cada dominio puede extraerse a un servicio propio sin reescribir la UI. Elegido sobre microservicios reales por la realidad de la **capa gratuita** (Render free duerme a 15 min y multiplica cold-starts/CORS por servicio). Ver ADR en §7.
|
| 7 |
>
|
docs/architect/architecture_agrovision_mvp.md
DELETED
|
@@ -1,158 +0,0 @@
|
|
| 1 |
-
# Arquitectura — AgroVisión MVP (Conteo por Dron)
|
| 2 |
-
|
| 3 |
-
> **Audiencia:** Arquitectos de solución, líderes técnicos, desarrolladores.
|
| 4 |
-
> **Alcance:** Estructura del **MVP** (conteo por dron + credenciales efímeras). Topología simplificada y síncrona. Para especificaciones funcionales, ver [`description_proyecto_agrovision_mvp.md`](../reference/description_proyecto_agrovision_mvp.md). Para la arquitectura objetivo completa, ver [`architecture_agrovision.md`](architecture_agrovision.md).
|
| 5 |
-
|
| 6 |
-
---
|
| 7 |
-
|
| 8 |
-
## 1. Visión General del Sistema (C4 – Nivel Contexto)
|
| 9 |
-
|
| 10 |
-
```mermaid
|
| 11 |
-
flowchart TB
|
| 12 |
-
subgraph Actores["Actores Principales"]
|
| 13 |
-
U1["👤 Agrónomo / Productor"]
|
| 14 |
-
end
|
| 15 |
-
|
| 16 |
-
subgraph Sistema["Sistema Central (MVP)"]
|
| 17 |
-
direction TB
|
| 18 |
-
APP["AgroVisión MVP<br/>─────────────────<br/>Carga de ortomosaico RGB +<br/>conteo de plantas con modelo predeterminado"]
|
| 19 |
-
end
|
| 20 |
-
|
| 21 |
-
subgraph Externos["Dependencias"]
|
| 22 |
-
MODEL["Modelo predeterminado<br/>─────────────<br/>agrovision-plantcount-v2.0.0.onnx (empaquetado, NO externo)"]
|
| 23 |
-
end
|
| 24 |
-
|
| 25 |
-
U1 -->|"Sube ortomosaico y obtiene conteo"| APP
|
| 26 |
-
APP -->|"Carga al iniciar (solo inferencia)"| MODEL
|
| 27 |
-
```
|
| 28 |
-
|
| 29 |
-
**Decisiones arquitectónicas clave (Nivel Macro):**
|
| 30 |
-
- **Sin dependencias externas para la función núcleo:** el conteo corre con el modelo empaquetado; no requiere llaves de API.
|
| 31 |
-
- **Inferencia síncrona:** sin cola ni worker — máxima simplicidad para validar el flujo.
|
| 32 |
-
- **Sin base de datos:** todo es efímero (memoria de sesión); refrescar borra todo.
|
| 33 |
-
- **Credenciales efímeras (placeholder):** la pestaña existe como base para módulos futuros, con el aviso de no-persistencia.
|
| 34 |
-
|
| 35 |
-
---
|
| 36 |
-
|
| 37 |
-
## 2. Componentes Internos (C4 – Nivel Contenedor)
|
| 38 |
-
|
| 39 |
-
```mermaid
|
| 40 |
-
flowchart LR
|
| 41 |
-
subgraph Cliente["Capa de Presentación"]
|
| 42 |
-
UI["UI — Shiny for Python<br/>──────────<br/>2 nav_panel: Conteo · Credenciales<br/>ui.input_file · estado efímero<br/>Host: ShinyApps.io"]
|
| 43 |
-
end
|
| 44 |
-
|
| 45 |
-
subgraph Backend["Capa de Aplicación"]
|
| 46 |
-
direction TB
|
| 47 |
-
API["Backend — FastAPI<br/>──────────<br/>POST /api/count (SÍNCRONO)<br/>Inferencia en proceso"]
|
| 48 |
-
MODEL["Modelo predeterminado<br/>agrovision-plantcount-v2.0.0.onnx (empaquetado)"]
|
| 49 |
-
end
|
| 50 |
-
|
| 51 |
-
UI -->|"HTTPS multipart (imagen)"| API
|
| 52 |
-
API -->|"carga al iniciar"| MODEL
|
| 53 |
-
```
|
| 54 |
-
|
| 55 |
-
**Flujo de una interacción típica:**
|
| 56 |
-
1. El agrónomo sube un ortomosaico en la UI y pulsa "Iniciar Conteo".
|
| 57 |
-
2. La UI envía `POST /api/count` (multipart) al backend.
|
| 58 |
-
3. El backend ejecuta RF-DETR-Nano **en proceso** (con *tiling* si es grande) y devuelve conteo + overlay (base64).
|
| 59 |
-
4. La UI renderiza métricas y overlay. **Nada se persiste.**
|
| 60 |
-
|
| 61 |
-
> **Variante monolítica:** para máxima simplicidad, UI + inferencia pueden vivir en **un solo contenedor** (Shiny llamando al modelo en proceso), evitando CORS y dos servicios.
|
| 62 |
-
|
| 63 |
-
---
|
| 64 |
-
|
| 65 |
-
## 3. Lógica Core / Procesos Críticos
|
| 66 |
-
|
| 67 |
-
```mermaid
|
| 68 |
-
flowchart TB
|
| 69 |
-
IN(["Ortomosaico RGB"])
|
| 70 |
-
P1["Validación (formato/tamaño) + tiling por GSD"]
|
| 71 |
-
P2["Inferencia RF-DETR-Nano (CPU, en proceso)"]
|
| 72 |
-
P3["Reensamble + NMS + heurística de fallas"]
|
| 73 |
-
P4["Conteo, densidad pl/Ha, malezas, % fallas"]
|
| 74 |
-
OUT(["Respuesta JSON + overlay base64"])
|
| 75 |
-
IN --> P1 --> P2 --> P3 --> P4 --> OUT
|
| 76 |
-
```
|
| 77 |
-
|
| 78 |
-
---
|
| 79 |
-
|
| 80 |
-
## 4. Flujo de Secuencia (Conteo Síncrono)
|
| 81 |
-
|
| 82 |
-
```mermaid
|
| 83 |
-
sequenceDiagram
|
| 84 |
-
actor U as Agrónomo
|
| 85 |
-
participant C as UI (Shiny)
|
| 86 |
-
participant S as Backend (FastAPI)
|
| 87 |
-
participant M as Modelo (RF-DETR-Nano)
|
| 88 |
-
|
| 89 |
-
U->>C: Sube ortomosaico + clic "Iniciar Conteo"
|
| 90 |
-
C->>S: POST /api/count (multipart, area_ha?)
|
| 91 |
-
S->>M: Inferencia en proceso (tiling)
|
| 92 |
-
M-->>S: boxes + count
|
| 93 |
-
S->>S: Calcula densidad, malezas, fallas + overlay
|
| 94 |
-
S-->>C: {count, density, weeds, failures, overlay_b64}
|
| 95 |
-
C-->>U: Render métricas + overlay (efímero)
|
| 96 |
-
```
|
| 97 |
-
|
| 98 |
-
---
|
| 99 |
-
|
| 100 |
-
## 5. Modelo de Dominio / Entidad-Relación
|
| 101 |
-
|
| 102 |
-
**El MVP no persiste datos.** No hay base de datos: la información de cada corrida vive en `reactive.value` de la sesión Shiny y se descarta al refrescar/cerrar.
|
| 103 |
-
|
| 104 |
-
> **Futuro:** al evolucionar a la plataforma completa, los conteos se persistirían en la tabla `plant_counts` definida en [`docs/db/diseno_db.md`](../db/diseno_db.md).
|
| 105 |
-
|
| 106 |
-
**Políticas de Datos (MVP):**
|
| 107 |
-
- **Efimeralidad total:** sin disco, sin BD, sin `localStorage`.
|
| 108 |
-
- **Aislamiento por sesión:** cada WebSocket Shiny es independiente.
|
| 109 |
-
|
| 110 |
-
---
|
| 111 |
-
|
| 112 |
-
## 6. Arquitectura de Despliegue (Infraestructura)
|
| 113 |
-
|
| 114 |
-
```mermaid
|
| 115 |
-
flowchart LR
|
| 116 |
-
subgraph Local["Entorno de Desarrollo"]
|
| 117 |
-
direction TB
|
| 118 |
-
DEV["docker-compose<br/>ui · api"]
|
| 119 |
-
SAMPLE[("sample_data/ + models/")]
|
| 120 |
-
DEV --- SAMPLE
|
| 121 |
-
end
|
| 122 |
-
|
| 123 |
-
subgraph Pipeline["CI/CD"]
|
| 124 |
-
direction TB
|
| 125 |
-
LINT["Ruff + pytest"]
|
| 126 |
-
BUILD["Build Docker images<br/>(modelo empaquetado)"]
|
| 127 |
-
LINT --> BUILD
|
| 128 |
-
end
|
| 129 |
-
|
| 130 |
-
subgraph Prod["Producción (Capa Gratuita)"]
|
| 131 |
-
direction TB
|
| 132 |
-
SHINY["ShinyApps.io (UI)"]
|
| 133 |
-
RENDER["Render (FastAPI + modelo)"]
|
| 134 |
-
SHINY -->|"HTTPS + CORS"| RENDER
|
| 135 |
-
end
|
| 136 |
-
|
| 137 |
-
Local -->|"git push"| Pipeline
|
| 138 |
-
Pipeline -->|"deploy"| Prod
|
| 139 |
-
```
|
| 140 |
-
|
| 141 |
-
**Notas de despliegue:**
|
| 142 |
-
- Dos servicios (UI + backend) **o** un único contenedor monolítico (Shiny + inferencia) en Render / Hugging Face Spaces.
|
| 143 |
-
- El modelo `agrovision-plantcount-v2.0.0.onnx` (ONNX ligero, **agnóstico** a la arquitectura: YOLO26/RF-DETR) cabe holgado en los 512 MB de Render.
|
| 144 |
-
- El **módulo de conteo arranca en standby** (`COUNTING_ENABLED=false`) hasta que el repo del modelo publique el artefacto en Hugging Face Hub. Licencia: AGPL-3.0 aceptada (app open-source).
|
| 145 |
-
|
| 146 |
-
---
|
| 147 |
-
|
| 148 |
-
## 7. Decisiones Arquitectónicas Relevantes (ADRs Resumidos)
|
| 149 |
-
|
| 150 |
-
| Decisión Tomada | Alternativa Descartada | Razón Principal |
|
| 151 |
-
| :--- | :--- | :--- |
|
| 152 |
-
| **Inferencia síncrona en proceso** | Cola PGMQ + worker (plataforma completa) | El MVP valida el flujo con mínima complejidad; el modelo ligero responde en segundos. |
|
| 153 |
-
| **Sin base de datos (efímero)** | Supabase PostGIS desde el inicio | El núcleo de valor (conteo) no requiere persistencia; evita configurar BYOK para la demo. |
|
| 154 |
-
| **Conteo sin llaves de API** | BYOK obligatorio | El modelo corre local; el usuario obtiene valor sin configurar credenciales. |
|
| 155 |
-
| **UI en Shiny for Python** | Streamlit / Astro | Coherencia con la plataforma completa; reutiliza la misma base de código de UI. |
|
| 156 |
-
| **Opción monolítica de despliegue** | Siempre dos servicios | Para el MVP, un contenedor único reduce CORS y complejidad operativa. |
|
| 157 |
-
|
| 158 |
-
> Todos los componentes del MVP son un **subconjunto estricto** de la [arquitectura completa](architecture_agrovision.md); evolucionar es activar módulos, no reescribir.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/architect/architecture_modelo_conteo_plantas.md
DELETED
|
@@ -1,192 +0,0 @@
|
|
| 1 |
-
# Arquitectura — Modelo de Conteo de Plantas (Repo Separado)
|
| 2 |
-
|
| 3 |
-
> **Audiencia:** Ingenieros de ML/visión, líderes técnicos.
|
| 4 |
-
> **Alcance:** Estructura del **repositorio de modelado** (pipeline ML), no de una app productiva. Su salida es **UN artefacto** (`agrovision-plantcount-*.onnx`) publicado en **Hugging Face Hub** y consumido por AgroVisión. Para especificaciones, ver [`description_proyecto_modelo_conteo_plantas.md`](../reference/description_proyecto_modelo_conteo_plantas.md).
|
| 5 |
-
> **Modelo (multi-candidato):** se evalúan **YOLO26** (AGPL-3.0, NMS-free), **RF-DETR** (Apache 2.0, NMS-free) y **DINOv3**; se publica **el mejor** (menor MAE). **AGPL aceptada** (app open-source) → YOLO26 usable. **Multi-cultivo** (arándano primero). DeepForest (MIT) para satélite. **Sin BD transaccional** (persistencia = datasets/artefactos versionados).
|
| 6 |
-
|
| 7 |
-
---
|
| 8 |
-
|
| 9 |
-
## 1. Visión General del Sistema (C4 – Nivel Contexto)
|
| 10 |
-
|
| 11 |
-
```mermaid
|
| 12 |
-
flowchart TB
|
| 13 |
-
subgraph Actores["Actores Principales"]
|
| 14 |
-
U1["👤 Ingeniero ML / Visión"]
|
| 15 |
-
end
|
| 16 |
-
|
| 17 |
-
subgraph Sistema["Sistema Central"]
|
| 18 |
-
APP["Repo de Modelo de Conteo<br/>─────────────────<br/>Notebooks + pipeline .py<br/>Entrena candidatos (YOLO26/RF-DETR), publica el mejor (1 modelo)"]
|
| 19 |
-
end
|
| 20 |
-
|
| 21 |
-
subgraph Externos["Dependencias Externas"]
|
| 22 |
-
DATA["Fuentes de Datos<br/>─────────────<br/>Roboflow (COCO) · Kaggle · DeepForest/NEON · MAXAR/Planet"]
|
| 23 |
-
GPU["Cómputo Gratuito<br/>─────────────<br/>Google Colab / Kaggle (GPU T4)"]
|
| 24 |
-
HF["Hugging Face Hub<br/>─────────────<br/>repo de modelos (artefacto versionado)"]
|
| 25 |
-
AV["AgroVisión (consumidor)<br/>─────────────<br/>hf_hub_download en build (solo inferencia)"]
|
| 26 |
-
end
|
| 27 |
-
|
| 28 |
-
U1 -->|"experimenta, entrena, exporta"| APP
|
| 29 |
-
APP <-->|"descarga datasets (COCO)"| DATA
|
| 30 |
-
APP <-->|"entrena en GPU"| GPU
|
| 31 |
-
APP -->|"publica 1 artefacto"| HF
|
| 32 |
-
HF -->|"build descarga el modelo"| AV
|
| 33 |
-
```
|
| 34 |
-
|
| 35 |
-
**Decisiones arquitectónicas clave (Nivel Macro):**
|
| 36 |
-
- **Notebooks-first:** validar viabilidad en notebooks (Colab/Kaggle) antes de promover a `.py`.
|
| 37 |
-
- **1 solo artefacto publicado:** se entrenan varios candidatos, se selecciona el mejor y **solo ese** se sube a HF Hub.
|
| 38 |
-
- **RF-DETR (Apache 2.0) NMS-free** como primario (permisivo, CPU) + **DINOv3/DeepForest** (satélite).
|
| 39 |
-
- **Nombre desacoplado:** `agrovision-plantcount` (no depende de la arquitectura interna).
|
| 40 |
-
|
| 41 |
-
---
|
| 42 |
-
|
| 43 |
-
## 2. Componentes Internos (C4 – Nivel Contenedor)
|
| 44 |
-
|
| 45 |
-
```mermaid
|
| 46 |
-
flowchart LR
|
| 47 |
-
subgraph Exp["Capa de Experimentación"]
|
| 48 |
-
NB["notebooks/<br/>──────────<br/>01 explore · 02 SAM→COCO · 03 RF-DETR · 04 satélite"]
|
| 49 |
-
end
|
| 50 |
-
|
| 51 |
-
subgraph Pipe["Capa de Pipeline (.py reproducible)"]
|
| 52 |
-
direction TB
|
| 53 |
-
ING["ingest.py (Roboflow COCO/Kaggle)"]
|
| 54 |
-
ANN["annotate_sam.py (SAM → COCO)"]
|
| 55 |
-
PRE["preprocess.py (tiling/splits/augment COCO)"]
|
| 56 |
-
TRN["train.py (RF-DETR detect/seg)"]
|
| 57 |
-
EVL["evaluate.py (mAP/F1/MAE + selección)"]
|
| 58 |
-
EXP2["export.py (.pth → ONNX + model card)"]
|
| 59 |
-
PUB["publish.py (→ Hugging Face Hub)"]
|
| 60 |
-
SAT["satellite/ (DeepForest + DINOv3)"]
|
| 61 |
-
end
|
| 62 |
-
|
| 63 |
-
subgraph Store["Almacenes / Artefactos"]
|
| 64 |
-
RAW[("data/raw")]
|
| 65 |
-
PROC[("data/processed (COCO)")]
|
| 66 |
-
MOD[("models/ (.onnx) + metrics.json")]
|
| 67 |
-
end
|
| 68 |
-
|
| 69 |
-
NB -.promueve.-> Pipe
|
| 70 |
-
ING --> RAW --> ANN --> PROC
|
| 71 |
-
PRE --> PROC
|
| 72 |
-
PROC --> TRN --> EVL --> EXP2 --> MOD --> PUB
|
| 73 |
-
SAT --> EVL
|
| 74 |
-
```
|
| 75 |
-
|
| 76 |
-
**Flujo de una interacción típica:**
|
| 77 |
-
1. El ingeniero **explora** datos y valida un conteo base en `03_train_rfdetr.ipynb`.
|
| 78 |
-
2. Promueve la lógica a `src/*.py`: `ingest` → `annotate_sam` (→ COCO) → `preprocess` → `train` → `evaluate` (**selecciona el mejor**) → `export` → `publish`.
|
| 79 |
-
3. `export.py` genera `agrovision-plantcount-vX.Y.Z.onnx`; `publish.py` lo sube a **HF Hub**.
|
| 80 |
-
4. El build de AgroVisión lo descarga con `hf_hub_download` como modelo predeterminado.
|
| 81 |
-
|
| 82 |
-
---
|
| 83 |
-
|
| 84 |
-
## 3. Lógica Core / Proceso Crítico (Pipeline ML)
|
| 85 |
-
|
| 86 |
-
```mermaid
|
| 87 |
-
flowchart TB
|
| 88 |
-
IN(["Datasets públicos (dron/satélite)"])
|
| 89 |
-
P1["Anotación SAM → formato COCO"]
|
| 90 |
-
P2["Preprocesamiento: tiling por GSD + augmentations + splits COCO"]
|
| 91 |
-
P3["Entrenamiento RF-DETR (transfer, NMS-free)"]
|
| 92 |
-
P4["Evaluación + SELECCIÓN del mejor (MAE/tamaño/latencia)"]
|
| 93 |
-
P5["Exportación ONNX + model card"]
|
| 94 |
-
P6["Publicación en Hugging Face Hub (1 artefacto)"]
|
| 95 |
-
OUT(["agrovision-plantcount-*.onnx (contrato §6)"])
|
| 96 |
-
IN --> P1 --> P2 --> P3 --> P4 --> P5 --> P6 --> OUT
|
| 97 |
-
P4 -.->|"si MAE > umbral"| P2
|
| 98 |
-
```
|
| 99 |
-
|
| 100 |
-
---
|
| 101 |
-
|
| 102 |
-
## 4. Flujo de Secuencia (Experimentación → Producción → Handoff)
|
| 103 |
-
|
| 104 |
-
```mermaid
|
| 105 |
-
sequenceDiagram
|
| 106 |
-
actor E as Ingeniero ML
|
| 107 |
-
participant NB as Notebook (Colab/Kaggle)
|
| 108 |
-
participant PY as Pipeline .py
|
| 109 |
-
participant GPU as GPU T4 (gratis)
|
| 110 |
-
participant HF as Hugging Face Hub
|
| 111 |
-
participant AV as AgroVisión (build)
|
| 112 |
-
|
| 113 |
-
E->>NB: PoC de conteo con dataset COCO público
|
| 114 |
-
NB->>GPU: entrena RFDETRNano (rápido)
|
| 115 |
-
GPU-->>NB: métricas base (mAP/MAE)
|
| 116 |
-
Note over NB,PY: viabilidad validada → promover lógica
|
| 117 |
-
E->>PY: ejecuta pipeline reproducible (seeds fijas)
|
| 118 |
-
PY->>GPU: train + eval completos
|
| 119 |
-
PY->>PY: evaluate.py SELECCIONA el mejor candidato
|
| 120 |
-
PY->>PY: export.py → agrovision-plantcount-v2.0.0.onnx
|
| 121 |
-
PY->>HF: publish.py sube 1 artefacto + model card
|
| 122 |
-
AV->>HF: hf_hub_download en docker build
|
| 123 |
-
AV-->>E: integrado como modelo predeterminado (MODEL_PATH)
|
| 124 |
-
```
|
| 125 |
-
|
| 126 |
-
---
|
| 127 |
-
|
| 128 |
-
## 5. Modelo de Dominio / Artefactos (no hay BD transaccional)
|
| 129 |
-
|
| 130 |
-
```mermaid
|
| 131 |
-
flowchart TB
|
| 132 |
-
subgraph Datos["Datos"]
|
| 133 |
-
DS["Dataset<br/>─────────<br/>fuente · licencia · clases (COCO)"]
|
| 134 |
-
ANN["Anotaciones COCO<br/>─────────<br/>_annotations.coco.json"]
|
| 135 |
-
end
|
| 136 |
-
subgraph Artefactos["Artefactos de Modelo"]
|
| 137 |
-
RUN["Run de Entrenamiento<br/>─────────<br/>hiperparámetros · seed · métricas"]
|
| 138 |
-
MOD["Modelo publicado (1)<br/>─────────<br/>agrovision-plantcount.onnx · model_version · architecture"]
|
| 139 |
-
CARD["Model Card<br/>─────────<br/>datos · licencia (Apache 2.0) · métricas"]
|
| 140 |
-
end
|
| 141 |
-
DS -->|"se anota"| ANN
|
| 142 |
-
ANN -->|"entrena"| RUN
|
| 143 |
-
RUN -->|"se selecciona el mejor → produce"| MOD
|
| 144 |
-
MOD -->|"documenta"| CARD
|
| 145 |
-
```
|
| 146 |
-
|
| 147 |
-
**Políticas de Datos:**
|
| 148 |
-
- **Versionado de datos:** `data/` no se commitea; se versiona el *manifiesto* (fuente, hash, licencia) y opcionalmente con **DVC**.
|
| 149 |
-
- **1 artefacto:** solo el modelo seleccionado se publica (HF Hub); su `model_version` (SemVer) enlaza run/dataset/métricas.
|
| 150 |
-
- **Reproducibilidad:** semillas fijas y config declarativa.
|
| 151 |
-
|
| 152 |
-
---
|
| 153 |
-
|
| 154 |
-
## 6. Arquitectura de Despliegue (Publicación de Artefactos)
|
| 155 |
-
|
| 156 |
-
```mermaid
|
| 157 |
-
flowchart LR
|
| 158 |
-
subgraph Local["Desarrollo"]
|
| 159 |
-
DEV["IDE + notebooks (CPU local)"]
|
| 160 |
-
end
|
| 161 |
-
subgraph Cloud["Cómputo Gratuito"]
|
| 162 |
-
COLAB["Colab / Kaggle (GPU T4)"]
|
| 163 |
-
end
|
| 164 |
-
subgraph CI["CI / Publicación"]
|
| 165 |
-
TEST["pytest (contrato + paridad)"]
|
| 166 |
-
PUB["publish.py → Hugging Face Hub vX.Y.Z"]
|
| 167 |
-
TEST --> PUB
|
| 168 |
-
end
|
| 169 |
-
subgraph Consumo["Consumo"]
|
| 170 |
-
AVB["Build de AgroVisión<br/>hf_hub_download → imagen backend"]
|
| 171 |
-
end
|
| 172 |
-
|
| 173 |
-
DEV -->|"PoC"| COLAB
|
| 174 |
-
COLAB -->|"checkpoint_best.pth"| CI
|
| 175 |
-
Local -->|"git push"| CI
|
| 176 |
-
PUB -->|"descarga artefacto"| AVB
|
| 177 |
-
```
|
| 178 |
-
|
| 179 |
-
---
|
| 180 |
-
|
| 181 |
-
## 7. Decisiones Arquitectónicas Relevantes (ADRs Resumidos)
|
| 182 |
-
|
| 183 |
-
| Decisión Tomada | Alternativa Descartada | Razón Principal |
|
| 184 |
-
| :--- | :--- | :--- |
|
| 185 |
-
| **Multi-candidato: YOLO26 + RF-DETR + DINOv3, se publica el mejor** | Fijar un solo modelo de entrada | Gana el de **menor MAE** (tamaño/latencia OK). **AGPL-3.0 aceptada** (app open-source) habilita YOLO26; RF-DETR (Apache) queda como alternativa permisiva. App **agnóstica por contrato**. |
|
| 186 |
-
| **Anotar a COCO + YOLO** | Un solo formato | Se anota una vez y se exporta a **COCO** (para RF-DETR) y **YOLO** (para YOLO26), permitiendo entrenar ambos candidatos. |
|
| 187 |
-
| **Publicar en Hugging Face Hub** | GitHub Releases / object storage | Hosting de modelos versionado y estándar; `hf_hub_download` integra en el build de AgroVisión. |
|
| 188 |
-
| **1 solo artefacto, el mejor** | Publicar todos los tracks | Simplicidad para el consumidor; los tracks son experimentos para elegir al ganador. |
|
| 189 |
-
| **Nombre desacoplado** (`agrovision-plantcount`) | Nombre por arquitectura (`rfdetr_nano`) | Permite cambiar de modelo sin tocar la app; el contrato es lo estable. |
|
| 190 |
-
| **DINOv3 + DeepForest** (satélite) | Entrenar conteo de árboles desde cero | DeepForest (MIT) preentrenado + DINOv3 satelital sin fine-tuning aceleran el track. |
|
| 191 |
-
| **Notebooks-first** | Ir directo a `.py` | Validar viabilidad barato antes de endurecer; cada notebook tiene su par `.py` testeado. |
|
| 192 |
-
| **Sin BD transaccional** | Postgres/registro pesado | El dominio son datasets/artefactos versionados (manifiestos + DVC + HF Hub). |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/db/diseno_db.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
|
| 3 |
> **Propósito:** Definir el modelo físico y lógico de persistencia de AgroVisión, justificar la elección del motor y servir como mapa de referencia para migraciones e implementación.
|
| 4 |
> **Origen:** Se construye a partir de [`docs/reference/description_proyecto_agrovision.md`](../reference/description_proyecto_agrovision.md).
|
| 5 |
-
> **Aplicabilidad:** Solo la **plataforma completa**. El
|
| 6 |
>
|
| 7 |
> **Alcance de construcción (esta iteración):** se implementan **todas** las tablas, pero con dos matices:
|
| 8 |
> - **`fields`, `ndvi_timeseries`, `chat_messages`** se usan activamente (parcelas, teledetección 5 años, agente).
|
|
|
|
| 2 |
|
| 3 |
> **Propósito:** Definir el modelo físico y lógico de persistencia de AgroVisión, justificar la elección del motor y servir como mapa de referencia para migraciones e implementación.
|
| 4 |
> **Origen:** Se construye a partir de [`docs/reference/description_proyecto_agrovision.md`](../reference/description_proyecto_agrovision.md).
|
| 5 |
+
> **Aplicabilidad:** Solo la **plataforma completa**. (El MVP histórico operaba en modo efímero sin base de datos.)
|
| 6 |
>
|
| 7 |
> **Alcance de construcción (esta iteración):** se implementan **todas** las tablas, pero con dos matices:
|
| 8 |
> - **`fields`, `ndvi_timeseries`, `chat_messages`** se usan activamente (parcelas, teledetección 5 años, agente).
|
docs/ejecucion.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
| 9 |
> **La app abre SIN credenciales** (verás los 6 módulos). Para *usar* cada módulo necesitas las llaves BYOK (todas de capa gratuita), que pones en `.env` (local) o en la pestaña **Credenciales** (sesión):
|
| 10 |
> - **Parcelas / Teledetección / Resumen** → Supabase (`DATABASE_URL`) y, para NDVI, **Copernicus** (`DEV_COPERNICUS_CLIENT_ID/SECRET`). El clima (Open-Meteo) no necesita llave.
|
| 11 |
> - **Asistente** → **Groq** (`DEV_GROQ_API_KEY`).
|
| 12 |
-
> - **Conteo** → **EN DESARROLLO** (standby): la pestaña muestra *"Módulo en preparación"*. Se habilita cuando el
|
| 13 |
|
| 14 |
---
|
| 15 |
|
|
|
|
| 9 |
> **La app abre SIN credenciales** (verás los 6 módulos). Para *usar* cada módulo necesitas las llaves BYOK (todas de capa gratuita), que pones en `.env` (local) o en la pestaña **Credenciales** (sesión):
|
| 10 |
> - **Parcelas / Teledetección / Resumen** → Supabase (`DATABASE_URL`) y, para NDVI, **Copernicus** (`DEV_COPERNICUS_CLIENT_ID/SECRET`). El clima (Open-Meteo) no necesita llave.
|
| 11 |
> - **Asistente** → **Groq** (`DEV_GROQ_API_KEY`).
|
| 12 |
+
> - **Conteo** → **EN DESARROLLO** (standby): la pestaña muestra *"Módulo en preparación"*. Se habilita cuando el **repo del modelo** (proyecto separado) publique el artefacto. Para demostrar el flujo con datos de prueba (mock) — ver §3.5.
|
| 13 |
|
| 14 |
---
|
| 15 |
|
docs/reference/description_proyecto_agrovision.md
CHANGED
|
@@ -4,11 +4,10 @@
|
|
| 4 |
>
|
| 5 |
> **Stack confirmado:** UI en **Shiny for Python**, backend/gateway en **FastAPI**, base de datos en **Supabase (PostgreSQL + PostGIS + Storage + Queues/PGMQ)**, agente conversacional con **Groq (Llama 3)** y teledetección con **Copernicus Sentinel-2 / NASA POWER / Open-Meteo**. Despliegue gratuito: **ShinyApps.io + Render + Supabase**.
|
| 6 |
>
|
| 7 |
-
> **Licencia y modelo:** AgroVisión es **open-source bajo AGPL-3.0** (lo que permite usar **YOLO26**). El modelo de conteo es **agnóstico**: la app consume el artefacto `agrovision-plantcount` (multi-candidato YOLO26/RF-DETR) por **contrato**, vía un adaptador de inferencia. El **módulo de conteo arranca en _standby_** (flag `COUNTING_ENABLED=false`) hasta que el
|
| 8 |
>
|
| 9 |
> **Documentos relacionados:**
|
| 10 |
-
> -
|
| 11 |
-
> - Repo del modelo de conteo (produce los pesos predeterminados): [`description_proyecto_modelo_conteo_plantas.md`](description_proyecto_modelo_conteo_plantas.md)
|
| 12 |
> - Investigación base: [`Plan Detallado Data Science Agrícola.md`](../investigation/Plan%20Detallado%20Data%20Science%20Agr%C3%ADcola.md)
|
| 13 |
> - Mockup interactivo: [`agrovisi_n_spa_prototype.html`](../investigation/agrovisi_n_spa_prototype.html)
|
| 14 |
|
|
@@ -453,7 +452,7 @@ uv run rsconnect deploy shiny ./frontend --name <cuenta> --title AgroVision-UI
|
|
| 453 |
|
| 454 |
### 7.4 Gestión del Modelo Predeterminado
|
| 455 |
|
| 456 |
-
* AgroVisión **no entrena**: consume pesos producidos por el
|
| 457 |
* El artefacto `agrovision-plantcount-v2.0.0.onnx` (RF-DETR-Nano exportado a ONNX) se **publica en Hugging Face Hub** y se descarga con `hf_hub_download` durante el build de la imagen del backend, copiándose a `/app/models/` (`MODEL_VERSION` fija la trazabilidad). El nombre es de **marca, desacoplado de la arquitectura** interna.
|
| 458 |
* **Contrato de inferencia** que el worker espera: entrada = tile RGB; salida = `{"boxes":[[x1,y1,x2,y2,conf,cls]], "count":int}` compatible con `plant_counts.result_json`.
|
| 459 |
|
|
|
|
| 4 |
>
|
| 5 |
> **Stack confirmado:** UI en **Shiny for Python**, backend/gateway en **FastAPI**, base de datos en **Supabase (PostgreSQL + PostGIS + Storage + Queues/PGMQ)**, agente conversacional con **Groq (Llama 3)** y teledetección con **Copernicus Sentinel-2 / NASA POWER / Open-Meteo**. Despliegue gratuito: **ShinyApps.io + Render + Supabase**.
|
| 6 |
>
|
| 7 |
+
> **Licencia y modelo:** AgroVisión es **open-source bajo AGPL-3.0** (lo que permite usar **YOLO26**). El modelo de conteo es **agnóstico**: la app consume el artefacto `agrovision-plantcount` (multi-candidato YOLO26/RF-DETR) por **contrato**, vía un adaptador de inferencia. El **módulo de conteo arranca en _standby_** (flag `COUNTING_ENABLED=false`) hasta que el **repo del modelo** (proyecto separado) publique el artefacto.
|
| 8 |
>
|
| 9 |
> **Documentos relacionados:**
|
| 10 |
+
> - El modelo de conteo se desarrolla en un **repo separado** (produce los pesos predeterminados).
|
|
|
|
| 11 |
> - Investigación base: [`Plan Detallado Data Science Agrícola.md`](../investigation/Plan%20Detallado%20Data%20Science%20Agr%C3%ADcola.md)
|
| 12 |
> - Mockup interactivo: [`agrovisi_n_spa_prototype.html`](../investigation/agrovisi_n_spa_prototype.html)
|
| 13 |
|
|
|
|
| 452 |
|
| 453 |
### 7.4 Gestión del Modelo Predeterminado
|
| 454 |
|
| 455 |
+
* AgroVisión **no entrena**: consume pesos producidos por el **repo del modelo de conteo** (proyecto separado).
|
| 456 |
* El artefacto `agrovision-plantcount-v2.0.0.onnx` (RF-DETR-Nano exportado a ONNX) se **publica en Hugging Face Hub** y se descarga con `hf_hub_download` durante el build de la imagen del backend, copiándose a `/app/models/` (`MODEL_VERSION` fija la trazabilidad). El nombre es de **marca, desacoplado de la arquitectura** interna.
|
| 457 |
* **Contrato de inferencia** que el worker espera: entrada = tile RGB; salida = `{"boxes":[[x1,y1,x2,y2,conf,cls]], "count":int}` compatible con `plant_counts.result_json`.
|
| 458 |
|
docs/reference/description_proyecto_agrovision_mvp.md
DELETED
|
@@ -1,237 +0,0 @@
|
|
| 1 |
-
# Documento de Definición Técnica — AgroVisión MVP (Conteo por Dron)
|
| 2 |
-
|
| 3 |
-
> **Propósito del documento:** Definir el alcance mínimo viable (MVP) de AgroVisión, centrado en el flujo de **carga de ortomosaico + conteo de plantas con modelo predeterminado**, más una pestaña de **credenciales efímeras**. Es la primera entrega del proyecto y la base sobre la que se construye la [plataforma completa](description_proyecto_agrovision.md).
|
| 4 |
-
>
|
| 5 |
-
> **Stack MVP:** UI en **Shiny for Python**, inferencia **síncrona** con un modelo predeterminado **agnóstico** (multi-candidato YOLO26/RF-DETR, definido en el repo del modelo), **sin base de datos obligatoria** (modo efímero). Despliegue gratuito en un solo servicio.
|
| 6 |
-
>
|
| 7 |
-
> **Standby:** el módulo de conteo **arranca en _standby_** (`COUNTING_ENABLED=false`) hasta que el [repo del modelo](description_proyecto_modelo_conteo_plantas.md) publique `agrovision-plantcount` en Hugging Face Hub. Licencia: AgroVisión **open-source AGPL-3.0** (permite YOLO26).
|
| 8 |
-
>
|
| 9 |
-
> **Documentos relacionados:**
|
| 10 |
-
> - Plataforma completa (objetivo final): [`description_proyecto_agrovision.md`](description_proyecto_agrovision.md)
|
| 11 |
-
> - Repo del modelo de conteo (produce los pesos): [`description_proyecto_modelo_conteo_plantas.md`](description_proyecto_modelo_conteo_plantas.md)
|
| 12 |
-
|
| 13 |
-
---
|
| 14 |
-
|
| 15 |
-
## 0. Resumen Ejecutivo
|
| 16 |
-
|
| 17 |
-
El MVP de AgroVisión valida el **núcleo de valor** de la plataforma con la mínima complejidad: el usuario sube un ortomosaico RGB de dron y obtiene, en segundos, el **conteo de plantas, la densidad estimada y un overlay con las detecciones**, usando un modelo de visión **predeterminado** (entrenado en un repo aparte). No requiere base de datos, ni cuentas externas para la función principal.
|
| 18 |
-
|
| 19 |
-
* **Propósito:** Demostrar el conteo automatizado de plantas de extremo a extremo, desplegable en capa gratuita, antes de incorporar teledetección y agente conversacional.
|
| 20 |
-
* **Usuarios Objetivos:** Agrónomos y productores que necesitan estimar densidad/emergencia de un lote.
|
| 21 |
-
* **Casos de Uso Core:**
|
| 22 |
-
1. El usuario **sube un ortomosaico RGB** del dron (`ui.input_file`).
|
| 23 |
-
2. El sistema ejecuta el **modelo de conteo predeterminado** (agnóstico: YOLO26/RF-DETR) y devuelve conteo, densidad (pl/Ha), malezas y % de fallas, con el overlay de *bounding boxes*.
|
| 24 |
-
3. El usuario revisa una pestaña de **Credenciales & APIs**: presente como base para módulos futuros (satélite, RAG), con el aviso de que **nada se guarda y que refrescar borra todo**.
|
| 25 |
-
|
| 26 |
-
> **Alcance excluido del MVP (roadmap):** teledetección Sentinel-2/NDVI, agente RAG (Groq), persistencia en Supabase, procesamiento asíncrono con PGMQ. Todo esto entra en la [plataforma completa](description_proyecto_agrovision.md).
|
| 27 |
-
|
| 28 |
-
---
|
| 29 |
-
|
| 30 |
-
## 1. Arquitectura de Componentes
|
| 31 |
-
|
| 32 |
-
El MVP usa una topología **simplificada**: una UI Shiny y un backend FastAPI con inferencia **síncrona** (sin worker ni cola). El modelo va empaquetado en la imagen del backend.
|
| 33 |
-
|
| 34 |
-
```mermaid
|
| 35 |
-
flowchart TD
|
| 36 |
-
classDef frontend fill:#BFDBFE,stroke:#2563EB,color:#1E3A8A
|
| 37 |
-
classDef gateway fill:#FDE047,stroke:#EAB308,color:#713F12
|
| 38 |
-
classDef service fill:#BBF7D0,stroke:#16A34A,color:#14532D
|
| 39 |
-
|
| 40 |
-
UI["💻 UI — Shiny for Python<br/>2 nav_panel: Conteo · Credenciales<br/>ui.input_file · estado efímero<br/>Host: ShinyApps.io (Free)"]:::frontend
|
| 41 |
-
|
| 42 |
-
GW["🔀 Backend — FastAPI<br/>POST /api/count (SÍNCRONO)<br/>Inferencia en proceso<br/>Host: Render (Free)"]:::gateway
|
| 43 |
-
|
| 44 |
-
MODEL["🧠 RF-DETR-Nano (.pt / ONNX)<br/>Modelo PREDETERMINADO<br/>Empaquetado en la imagen"]:::service
|
| 45 |
-
|
| 46 |
-
UI <-->|"HTTPS multipart (imagen)"| GW
|
| 47 |
-
GW -->|"Carga al iniciar"| MODEL
|
| 48 |
-
```
|
| 49 |
-
|
| 50 |
-
### 1.1 Glosario de Módulos
|
| 51 |
-
|
| 52 |
-
| Componente | Responsabilidad | Tecnologías | Despliegue |
|
| 53 |
-
| :--- | :--- | :--- | :--- |
|
| 54 |
-
| **UI (Shiny)** | Carga de imagen, disparo del conteo, render de métricas y overlay. Estado **efímero** en memoria. | Shiny for Python, `httpx`, Pillow | ShinyApps.io Free |
|
| 55 |
-
| **Backend (FastAPI)** | Recibe la imagen, ejecuta inferencia **síncrona**, devuelve conteo + overlay (imagen anotada en base64 o multipart). | FastAPI, **adaptador de inferencia** (onnxruntime o `ultralytics` según arquitectura), OpenCV | Render Free |
|
| 56 |
-
| **Modelo predeterminado** | Pesos de **solo inferencia**, cargados vía adaptador (**agnóstico**: YOLO26/RF-DETR). | `agrovision-plantcount` (ONNX) | Artefacto del repo del modelo (Hugging Face Hub); AGPL-3.0 aceptada |
|
| 57 |
-
|
| 58 |
-
> **Alternativa monolítica:** para el MVP es válido empaquetar UI + inferencia en un **único contenedor** (Shiny llamando al modelo en proceso) y desplegarlo en un solo host Docker (p. ej. Render o Hugging Face Spaces). La versión de dos servicios se prefiere si se quiere reutilizar el backend en la plataforma completa.
|
| 59 |
-
|
| 60 |
-
---
|
| 61 |
-
|
| 62 |
-
## 2. Flujo de Datos
|
| 63 |
-
|
| 64 |
-
### 2.1 Orígenes y Destinos
|
| 65 |
-
|
| 66 |
-
* **Entradas:** ortomosaico RGB (`ui.input_file`); opcionalmente parámetros de vuelo (altura, GSD) para convertir conteo a densidad; área del lote (Ha) ingresada manualmente.
|
| 67 |
-
* **Salidas:** conteo total, densidad pl/Ha, # malezas, % fallas, imagen con *bounding boxes*. **Nada se persiste**: todo vive en la sesión y se descarta al refrescar.
|
| 68 |
-
|
| 69 |
-
### 2.2 Grafo Reactivo (síncrono)
|
| 70 |
-
|
| 71 |
-
```mermaid
|
| 72 |
-
flowchart LR
|
| 73 |
-
A["input.file_upload"] --> B["@reactive.calc validación"]
|
| 74 |
-
B --> C["POST /api/count (multipart)"]
|
| 75 |
-
C --> D["FastAPI: inferencia RF-DETR-Nano en proceso"]
|
| 76 |
-
D --> E["conteo + overlay (base64)"]
|
| 77 |
-
E --> F["@render.image / @render.ui métricas"]
|
| 78 |
-
```
|
| 79 |
-
|
| 80 |
-
---
|
| 81 |
-
|
| 82 |
-
## 3. Modelo de Datos
|
| 83 |
-
|
| 84 |
-
**El MVP no requiere base de datos.** Toda la información de una corrida (imagen, conteo, métricas) vive en `reactive.value` de la sesión Shiny y se descarta al cerrar/refrescar.
|
| 85 |
-
|
| 86 |
-
> **Persistencia opcional (futuro):** si el usuario configura su propio Supabase, se reutilizaría la tabla `plant_counts` definida en la [plataforma completa](description_proyecto_agrovision.md#3-modelo-de-datos-bases-de-datos). En el MVP queda fuera de alcance.
|
| 87 |
-
|
| 88 |
-
---
|
| 89 |
-
|
| 90 |
-
## 4. Contratos de API
|
| 91 |
-
|
| 92 |
-
| Método | Path | Descripción | Entrada | Respuesta |
|
| 93 |
-
| :--- | :--- | :--- | :--- | :--- |
|
| 94 |
-
| `GET` | `/api/status` | Healthcheck + versión del modelo. | — | `{"status":"ok","model":"agrovision-plantcount","version":"2.0.0"}` |
|
| 95 |
-
| `POST` | `/api/count` | Inferencia **síncrona** de conteo. | `multipart/form-data` (imagen) + `area_ha?` (form) | `{"count":124,"density":72400,"weeds":12,"failures":1.2,"confidence":0.91,"overlay_b64":"<png>"}` |
|
| 96 |
-
|
| 97 |
-
> **CORS:** `CORSMiddleware` con `allow_origins=[<ShinyApps.io>, "http://localhost:8000"]`. En el MVP la función principal (conteo) **no requiere llaves de usuario**, porque el modelo corre local; las cabeceras `X-User-*` se reservan para módulos futuros.
|
| 98 |
-
|
| 99 |
-
---
|
| 100 |
-
|
| 101 |
-
## 5. Lógica de Negocio
|
| 102 |
-
|
| 103 |
-
### 5.1 Conteo e Inferencia
|
| 104 |
-
|
| 105 |
-
El backend carga el modelo (ONNX) una sola vez al iniciar mediante un **adaptador de inferencia** — **agnóstico** a la arquitectura (onnxruntime, o `ultralytics` si el ganador es YOLO). Por cada imagen ejecuta detección (YOLO26/RF-DETR son **NMS-free**), cuenta cajas por clase (planta/arbusto, maleza), y para ortomosaicos grandes aplica *tiling* para respetar la RAM de Render (512 MB). **Mientras el módulo esté en standby**, este flujo está deshabilitado.
|
| 106 |
-
|
| 107 |
-
### 5.2 Densidad y GSD
|
| 108 |
-
|
| 109 |
-
$$Densidad_{pl/Ha} = \frac{Conteo_{plantas}}{Área_{Ha}}$$
|
| 110 |
-
|
| 111 |
-
El área puede ingresarse manualmente o derivarse del GSD y la cobertura de la imagen:
|
| 112 |
-
|
| 113 |
-
$$GSD = \frac{S_w \times H}{f \times I_w}$$
|
| 114 |
-
|
| 115 |
-
donde $S_w$ = ancho de sensor (mm), $H$ = altura de vuelo (m), $f$ = focal (mm), $I_w$ = ancho de imagen (px).
|
| 116 |
-
|
| 117 |
-
### 5.3 Reglas de Validación
|
| 118 |
-
|
| 119 |
-
1. **Formato/tamaño:** solo `JPG/PNG/TIFF`; límite de tamaño en `ui.input_file` (p. ej. ≤ 50 MB) para no agotar RAM.
|
| 120 |
-
2. **Confianza mínima:** detecciones bajo umbral (p. ej. `conf < 0.25`) se descartan.
|
| 121 |
-
3. **Fallas de siembra:** se estiman como huecos en hileras detectadas (heurística sobre el patrón de cajas).
|
| 122 |
-
|
| 123 |
-
---
|
| 124 |
-
|
| 125 |
-
## 6. Interfaz de Usuario (UI/UX)
|
| 126 |
-
|
| 127 |
-
UI con **2 pestañas** (`ui.page_navbar`):
|
| 128 |
-
|
| 129 |
-
1. **Detección Dron (Conteo)** — **arranca en _standby_** (aviso "Módulo en preparación") hasta `COUNTING_ENABLED=true`. Activo: zona de carga (`ui.input_file`), botón "Iniciar Conteo", visualizador del overlay con *bounding boxes*, panel de métricas (total, densidad, malezas, fallas, confianza). El modelo es **predeterminado** y **agnóstico** (YOLO26/RF-DETR), creado en el repo aparte; cultivo inicial: arándano.
|
| 130 |
-
2. **Credenciales & APIs** — formularios (deshabilitados/placeholder para satélite y RAG en el MVP) con el aviso de efimeralidad.
|
| 131 |
-
|
| 132 |
-
### 6.1 Layout (Wireframe ASCII)
|
| 133 |
-
|
| 134 |
-
```text
|
| 135 |
-
┌──────────────────────────────────────────────────────────────────────┐
|
| 136 |
-
│ 🌱 AgroVisión MVP [👤 Agrónomo] │
|
| 137 |
-
├──────────────┬───────────────────────────────────────────────────────┤
|
| 138 |
-
│ ▸ Dron YOLO │ [📁 Subir ortomosaico] [▶ Iniciar Conteo] │
|
| 139 |
-
│ ▸ Credenciales│ ┌───────────────────────┐ ┌─ Métricas ───────────┐ │
|
| 140 |
-
│ │ │ Overlay con │ │ Total: 124 │ │
|
| 141 |
-
│ │ │ bounding boxes │ │ Densidad: 72,400 /Ha │ │
|
| 142 |
-
│ │ │ (Signed/base64) │ │ Malezas: 12 │ │
|
| 143 |
-
│ │ └───────────────────────┘ │ Fallas: 1.2% │ │
|
| 144 |
-
│ │ └──────────────────────┘ │
|
| 145 |
-
└──────────────┴───────────────────────────────────────────────────────┘
|
| 146 |
-
```
|
| 147 |
-
|
| 148 |
-
### 6.2 Efimeralidad (idéntica a la plataforma completa)
|
| 149 |
-
|
| 150 |
-
* Estado en `reactive.value` (memoria de sesión). Sin `localStorage`, sin cookies persistentes.
|
| 151 |
-
* **Refrescar la página = nueva sesión = todo borrado.** La pestaña de Credenciales lo advierte:
|
| 152 |
-
> ⚠️ *Tus credenciales y resultados se usan solo durante esta sesión. **No se guardan ni almacenan.** Si actualizas o cierras la página, todo se borrará.*
|
| 153 |
-
|
| 154 |
-
---
|
| 155 |
-
|
| 156 |
-
## 7. Configuración de Entornos y Despliegue
|
| 157 |
-
|
| 158 |
-
### 7.1 Variables de Entorno (`.env.example`)
|
| 159 |
-
|
| 160 |
-
```bash
|
| 161 |
-
APP_ENV=development
|
| 162 |
-
LOG_LEVEL=info
|
| 163 |
-
|
| 164 |
-
# UI (Shiny)
|
| 165 |
-
SHINY_PORT=8001
|
| 166 |
-
API_BASE_URL=http://localhost:8000 # En prod: URL del backend en Render
|
| 167 |
-
|
| 168 |
-
# Backend (FastAPI)
|
| 169 |
-
API_PORT=8000
|
| 170 |
-
ALLOWED_ORIGINS=http://localhost:8001,https://<tu-app>.shinyapps.io
|
| 171 |
-
|
| 172 |
-
# Modelo predeterminado (descargado de Hugging Face Hub en el build)
|
| 173 |
-
MODEL_PATH=/app/models/agrovision-plantcount-v2.0.0.onnx
|
| 174 |
-
MODEL_VERSION=2.0.0
|
| 175 |
-
HF_MODEL_REPO=<org>/agrovision-plantcount
|
| 176 |
-
COUNTING_ENABLED=false # standby hasta que el modelo esté publicado
|
| 177 |
-
```
|
| 178 |
-
|
| 179 |
-
### 7.2 Docker (Local)
|
| 180 |
-
|
| 181 |
-
```yaml
|
| 182 |
-
# docker-compose.yml (MVP)
|
| 183 |
-
services:
|
| 184 |
-
api:
|
| 185 |
-
build: ./backend
|
| 186 |
-
ports: ["8000:8000"]
|
| 187 |
-
env_file: .env
|
| 188 |
-
volumes: ["./models:/models:ro", "./sample_data:/data:ro"]
|
| 189 |
-
ui:
|
| 190 |
-
build: ./frontend
|
| 191 |
-
ports: ["8001:8001"]
|
| 192 |
-
environment: ["API_BASE_URL=http://api:8000"]
|
| 193 |
-
```
|
| 194 |
-
|
| 195 |
-
> Incluye `sample_data/` con ortomosaicos de ejemplo para probar el conteo sin credenciales. El modelo se monta read-only desde `models/`.
|
| 196 |
-
|
| 197 |
-
### 7.3 Producción (Capa Gratuita)
|
| 198 |
-
|
| 199 |
-
| Componente | Plataforma | Límites | Caveat |
|
| 200 |
-
| :--- | :--- | :--- | :--- |
|
| 201 |
-
| **UI (Shiny)** | [ShinyApps.io Free](https://support.posit.co/hc/en-us/articles/217592947-What-are-the-limits-of-the-shinyapps-io-Free-plan) | 5 apps · 25 h activas/mes | App ASGI nativa; `rsconnect deploy shiny`. |
|
| 202 |
-
| **Backend** | [Render Free](https://render.com/docs/free) | 512 MB · duerme a 15 min · 750 h/mes | *Cold start* 30–60 s; RF-DETR-Nano (ONNX ligero) cabe holgado. |
|
| 203 |
-
|
| 204 |
-
```powershell
|
| 205 |
-
# UI -> ShinyApps.io
|
| 206 |
-
uv run rsconnect deploy shiny ./frontend --name <cuenta> --title AgroVision-MVP
|
| 207 |
-
|
| 208 |
-
# Backend -> Render (Dockerfile detectado automáticamente)
|
| 209 |
-
```
|
| 210 |
-
|
| 211 |
-
> **Opción aún más simple:** desplegar el MVP como **monolito** (Shiny + inferencia en proceso) en un único contenedor Docker (Render o Hugging Face Spaces), evitando CORS y dos servicios.
|
| 212 |
-
|
| 213 |
-
### 7.4 Modelo Predeterminado
|
| 214 |
-
|
| 215 |
-
El MVP **no entrena**. Consume `agrovision-plantcount-v2.0.0.onnx` (arquitectura según el repo del modelo: YOLO26/RF-DETR) producido por el [repo del modelo](description_proyecto_modelo_conteo_plantas.md) y **publicado en Hugging Face Hub**; se descarga con `hf_hub_download` en el build y se carga vía el adaptador de inferencia. Contrato de inferencia: entrada = tile RGB; salida = `{"boxes":[[x1,y1,x2,y2,conf,cls]], "count":int}`.
|
| 216 |
-
|
| 217 |
-
---
|
| 218 |
-
|
| 219 |
-
## 8. Camino del MVP a la Plataforma Completa
|
| 220 |
-
|
| 221 |
-
| Capacidad | MVP | Plataforma completa |
|
| 222 |
-
| :--- | :--- | :--- |
|
| 223 |
-
| Conteo por dron (modelo predeterminado, agnóstico) | ✅ | ✅ |
|
| 224 |
-
| Credenciales efímeras (BYOK) | ✅ (placeholder) | ✅ (activas) |
|
| 225 |
-
| Inferencia | Síncrona en proceso | Asíncrona (PGMQ + worker) |
|
| 226 |
-
| Teledetección Sentinel-2 / NDVI | ❌ | ✅ |
|
| 227 |
-
| Agente RAG (Groq/Llama 3) | ❌ | ✅ |
|
| 228 |
-
| Persistencia (Supabase PostGIS + Storage) | ❌ | ✅ |
|
| 229 |
-
| Módulos UI | 2 | 5 |
|
| 230 |
-
|
| 231 |
-
El MVP comparte la misma base de código de UI y backend; activar los módulos restantes es incremental.
|
| 232 |
-
|
| 233 |
-
---
|
| 234 |
-
|
| 235 |
-
## Apéndice — Fuentes
|
| 236 |
-
|
| 237 |
-
Basado en [`Plan Detallado Data Science Agrícola.md`](../investigation/Plan%20Detallado%20Data%20Science%20Agr%C3%ADcola.md) y el [mockup](../investigation/agrovisi_n_spa_prototype.html). Límites de capa gratuita verificados: [ShinyApps.io](https://support.posit.co/hc/en-us/articles/217592947-What-are-the-limits-of-the-shinyapps-io-Free-plan), [Render](https://render.com/docs/free).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/reference/description_proyecto_modelo_conteo_plantas.md
DELETED
|
@@ -1,297 +0,0 @@
|
|
| 1 |
-
# Documento de Definición Técnica — Modelo de Conteo de Plantas (Repo Separado)
|
| 2 |
-
|
| 3 |
-
> **Propósito del documento:** Definir el repositorio **independiente** cuyo único objetivo es **crear, entrenar, evaluar y exportar el modelo de visión** que cuenta plantas a partir de imágenes RGB de **dron** (y árboles/copas desde **satélite de alta resolución**). Produce y publica **UN solo artefacto** (el mejor candidato) que consume AgroVisión; no expone una app productiva.
|
| 4 |
-
>
|
| 5 |
-
> **Enfoque multi-candidato (se publica el mejor):** se entrenan/evalúan **YOLO26** (Ultralytics, **AGPL-3.0**, NMS-free, detect/seg), **RF-DETR** (Roboflow, **Apache 2.0**, NMS-free, detect/seg) y **DINOv3** (backbone SSL + satelital); gana el de **menor MAE de conteo** que cumpla tamaño/latencia, y **solo ese se publica**. **DeepForest** (MIT) es baseline para árboles en satélite. Anotación con **SAM**.
|
| 6 |
-
>
|
| 7 |
-
> **Licencia:** AgroVisión **acepta AGPL-3.0** (la app es open-source), por lo que **YOLO26 es usable**. La licencia efectiva del artefacto publicado depende del candidato ganador (AGPL si YOLO26; Apache si RF-DETR) y se declara en la *model card*.
|
| 8 |
-
>
|
| 9 |
-
> **Multi-cultivo (arándano primero):** el modelo es **multi-clase/multi-cultivo**. Foco inicial: **arándano** (arbustos en hilera — relevante para Hortifrut). Se generaliza a otros cultivos cambiando **dataset + lista de clases**.
|
| 10 |
-
>
|
| 11 |
-
> **Publicación:** **1 artefacto** `agrovision-plantcount-vX.Y.Z.onnx` en **Hugging Face Hub**; AgroVisión lo descarga en su **build de Docker** (`hf_hub_download`) y lo consume por **contrato** (nombre de marca **desacoplado de la arquitectura**). Enfoque **notebooks-first**.
|
| 12 |
-
>
|
| 13 |
-
> **Documentos relacionados:** [`description_proyecto_agrovision.md`](description_proyecto_agrovision.md) · [`architecture_modelo_conteo_plantas.md`](../architect/architecture_modelo_conteo_plantas.md) · [`plan_modelo_conteo_plantas.md`](../plan/plan_modelo_conteo_plantas.md) · [`tareas_modelo_conteo_plantas.md`](../task/tareas_modelo_conteo_plantas.md) · [`Plan Detallado`](../investigation/Plan%20Detallado%20Data%20Science%20Agr%C3%ADcola.md)
|
| 14 |
-
|
| 15 |
-
---
|
| 16 |
-
|
| 17 |
-
## 0. Resumen Ejecutivo
|
| 18 |
-
|
| 19 |
-
Este repositorio implementa el **pipeline de Machine Learning** para el conteo automatizado de plantas, con **dos modalidades**:
|
| 20 |
-
|
| 21 |
-
1. **Dron RGB (primaria):** conteo de plantas/arbustos individuales en ortomosaicos de baja altitud (~15–30 m) con un **detector** (candidatos: YOLO26 / RF-DETR). Cultivo inicial: **arándano**.
|
| 22 |
-
2. **Satélite de alta resolución (secundaria):** conteo de árboles/copas en imágenes sub-métricas (MAXAR/Planet) con **DeepForest** y/o **DINOv3 satelital**.
|
| 23 |
-
|
| 24 |
-
> ⚠️ **Resolución:** el conteo individual requiere alta resolución (dron, o satélite sub-métrico). El **Sentinel-2 gratuito (10 m) NO sirve** para contar plantas; solo índices/área (eso vive en la teledetección de AgroVisión).
|
| 25 |
-
|
| 26 |
-
* **Propósito:** Producir y **publicar el mejor modelo** de conteo (multi-candidato, multi-cultivo) como artefacto **predeterminado de solo inferencia** para AgroVisión.
|
| 27 |
-
* **Estrategia "1 modelo, el mejor":** se entrenan varios candidatos (YOLO26, RF-DETR; opc. variantes seg) y `evaluate.py` **selecciona el de menor MAE de conteo** que cumpla tamaño/latencia en CPU; **solo ese se publica**. El satélite reusa pesos de **DeepForest** (no es artefacto propio).
|
| 28 |
-
* **Multi-cultivo:** clases configurables; **arándano** primero, luego maíz/girasol/etc. (ver §2.3).
|
| 29 |
-
* **Metodología notebooks-first:** (1) experimentar en notebooks; (2) endurecer en `.py`; (3) entrenar/evaluar/seleccionar/exportar/publicar.
|
| 30 |
-
* **Entregable principal:** `agrovision-plantcount-vX.Y.Z.onnx` en **Hugging Face Hub** + *model card* (con arquitectura, licencia, cultivo, métricas) + contrato.
|
| 31 |
-
|
| 32 |
-
> **Frontera de responsabilidad:** este repo **entrena y publica**; AgroVisión **solo infiere** (agnóstico a la arquitectura, por contrato). YOLO26 y RF-DETR son **NMS-free**, así que el contrato no requiere NMS.
|
| 33 |
-
|
| 34 |
-
---
|
| 35 |
-
|
| 36 |
-
## 1. Arquitectura del Pipeline (Modular, Notebooks-First)
|
| 37 |
-
|
| 38 |
-
```mermaid
|
| 39 |
-
flowchart TD
|
| 40 |
-
classDef nb fill:#FEF3C7,stroke:#D97706,color:#7C2D12
|
| 41 |
-
classDef data fill:#BFDBFE,stroke:#2563EB,color:#1E3A8A
|
| 42 |
-
classDef proc fill:#BBF7D0,stroke:#16A34A,color:#14532D
|
| 43 |
-
classDef model fill:#FDE047,stroke:#EAB308,color:#713F12
|
| 44 |
-
classDef out fill:#E9D5FF,stroke:#7C3AED,color:#4C1D95
|
| 45 |
-
|
| 46 |
-
subgraph EXP["🧪 Experimentación (notebooks/ — PRIMERO)"]
|
| 47 |
-
NB1["01_explore_data.ipynb"]:::nb
|
| 48 |
-
NB2["02_annotate_sam.ipynb<br/>SAM → COCO/YOLO"]:::nb
|
| 49 |
-
NB3["03_train_candidates.ipynb<br/>PoC YOLO26 + RF-DETR (Colab GPU)"]:::nb
|
| 50 |
-
NB4["04_satellite_deepforest_dinov3.ipynb"]:::nb
|
| 51 |
-
end
|
| 52 |
-
|
| 53 |
-
D1["📦 Datasets por cultivo<br/>Arándano · Roboflow · Kaggle · DeepForest"]:::data
|
| 54 |
-
A1["✂️ Anotación SAM → COCO + YOLO<br/>(COCO para RF-DETR, YOLO para YOLO26)"]:::proc
|
| 55 |
-
P1["🔧 Preprocesamiento<br/>tiling por GSD · augmentations · splits · clases por cultivo"]:::proc
|
| 56 |
-
|
| 57 |
-
M1["🧠 Candidato A — YOLO26 (AGPL)<br/>detect/seg, NMS-free"]:::model
|
| 58 |
-
M2["🧠 Candidato B — RF-DETR (Apache)<br/>detect/seg, NMS-free"]:::model
|
| 59 |
-
M3["🧠 Track C — DINOv3 / DeepForest<br/>dominio + satélite (árboles)"]:::model
|
| 60 |
-
|
| 61 |
-
E1["📊 Evaluación + SELECCIÓN<br/>menor MAE / tamaño / latencia → el mejor"]:::proc
|
| 62 |
-
X1["🚀 Exportación + Publicación<br/>1 artefacto .onnx → Hugging Face Hub"]:::out
|
| 63 |
-
AV["⬇️ AgroVisión (build: hf_hub_download)"]:::out
|
| 64 |
-
|
| 65 |
-
EXP -.promueve lógica validada.-> D1
|
| 66 |
-
D1 --> A1 --> P1
|
| 67 |
-
P1 --> M1 & M2 & M3
|
| 68 |
-
M1 & M2 & M3 --> E1 --> X1 --> AV
|
| 69 |
-
```
|
| 70 |
-
|
| 71 |
-
### 1.1 Glosario de Etapas
|
| 72 |
-
|
| 73 |
-
| Etapa | Responsabilidad | Tecnologías | Salida |
|
| 74 |
-
| :--- | :--- | :--- | :--- |
|
| 75 |
-
| **Experimentación (notebooks)** | Validar viabilidad por cultivo. | Jupyter, Colab/Kaggle (GPU T4) | Notebooks + hallazgos |
|
| 76 |
-
| **Ingesta** | Descargar/versionar datasets por cultivo. | `roboflow`, `kagglehub`, `deepforest`, DVC (opc.) | `data/raw/<cultivo>/` |
|
| 77 |
-
| **Anotación** | Etiquetado semi-automático. | **SAM**, OpenCV | **COCO** (RF-DETR) + **YOLO** (YOLO26) |
|
| 78 |
-
| **Preprocesamiento** | Tiling por GSD, augmentations, splits, clases. | `albumentations`, `supervision` | `data/processed/<cultivo>/` |
|
| 79 |
-
| **Entrenamiento (candidatos)** | *Fine-tuning* de YOLO26 y RF-DETR. | **`ultralytics`** (YOLO26), **`rfdetr`** | checkpoints por candidato |
|
| 80 |
-
| **Evaluación + selección** | Métricas + elegir el mejor. | supervision, scikit-learn | candidato ganador |
|
| 81 |
-
| **Exportación / publicación** | ONNX + model card + **HF Hub**. | `onnx`, `huggingface_hub` | `agrovision-plantcount-*.onnx` |
|
| 82 |
-
|
| 83 |
-
---
|
| 84 |
-
|
| 85 |
-
## 2. Flujo de Datos e Integración
|
| 86 |
-
|
| 87 |
-
### 2.1 Orígenes y Destinos
|
| 88 |
-
* **Entradas:** datasets aéreos/satelitales por cultivo (RGB), parámetros de captura (GSD), anotaciones SAM.
|
| 89 |
-
* **Salidas:** dataset anotado (COCO + YOLO), checkpoints por candidato, **1** `.onnx` publicado, métricas y *model card*.
|
| 90 |
-
|
| 91 |
-
### 2.2 Dónde Conseguir las Imágenes (sin dataset propio)
|
| 92 |
-
|
| 93 |
-
| Fuente | Cultivo/Modalidad | Acceso |
|
| 94 |
-
| :--- | :--- | :--- |
|
| 95 |
-
| **Roboflow Universe** (aerial/agriculture) | Multi-cultivo, export **COCO y YOLO** | [universe.roboflow.com](https://universe.roboflow.com/browse/aerial/agriculture) |
|
| 96 |
-
| **Arándano — estudios con dron** | *Bush Model* + *Berry Model* (YOLO); BerryNet | [arXiv 2501.02344](https://arxiv.org/abs/2501.02344) |
|
| 97 |
-
| **Kaggle** | Plant Detection & Counting (maíz/girasol), Plantations | `kagglehub` |
|
| 98 |
-
| **CitDet** | Cítricos (frutos) | [arXiv 2309.05645](https://arxiv.org/html/2309.05645v3) |
|
| 99 |
-
| **DeepForest / NEON** | Árboles (aéreo/satélite, MIT) | [github.com/weecology/DeepForest](https://github.com/weecology/DeepForest) |
|
| 100 |
-
| **MAXAR Open Data / Planet NICFI** | Satélite sub-métrico (árboles) | Programas gratuitos |
|
| 101 |
-
| **Sentinel-2 / Copernicus** | ⚠️ Solo índices/área (no conteo) | API STAC |
|
| 102 |
-
|
| 103 |
-
> **Datos propios de Hortifrut:** cuando existan ortomosaicos de campos de arándano, se anotan con SAM y se integran como dataset prioritario.
|
| 104 |
-
|
| 105 |
-
### 2.3 Cultivos Objetivo (multi-cultivo configurable)
|
| 106 |
-
|
| 107 |
-
El modelo es **multi-clase**; cada cultivo es un dataset + `data.yaml`/`categories` propio. Cultivos validados en la literatura para conteo con dron:
|
| 108 |
-
|
| 109 |
-
| Grupo | Cultivos | Paradigma |
|
| 110 |
-
| :--- | :--- | :--- |
|
| 111 |
-
| **Berries (foco Hortifrut)** | **Arándano** (arbustos; opc. bayas/yield), frambuesa, mora, fresa | Detección (arbusto) / densidad (bayas) |
|
| 112 |
-
| **Hilera / plántulas** | Maíz, girasol, remolacha, sorgo, algodón, soya, trigo, arroz, frijol, papa | Detección (stand count) |
|
| 113 |
-
| **Frutales / árboles** | Cítricos, palma, vid (racimos) | Detección / DeepForest |
|
| 114 |
-
| **Genérico denso** | Escenas muy densas (bayas, espigas) | Densidad (tipo TasselNetV4) |
|
| 115 |
-
|
| 116 |
-
> **Orden recomendado:** (1) **arándano** (detección de arbustos en hilera, MVP del modelo); (2) generalizar a maíz/girasol; (3) opcional: track de **densidad** para conteo de bayas/yield.
|
| 117 |
-
|
| 118 |
-
### 2.4 GSD
|
| 119 |
-
|
| 120 |
-
$$GSD = \frac{S_w \times H}{f \times I_w}$$
|
| 121 |
-
|
| 122 |
-
$S_w$ ancho de sensor (mm), $H$ altura (m), $f$ focal (mm), $I_w$ ancho de imagen (px). El GSD guía el **tamaño de *tile*** (cada planta/arbusto con suficientes píxeles). En arándano, la altura de vuelo se ajusta para que cada arbusto sea claramente resoluble.
|
| 123 |
-
|
| 124 |
-
---
|
| 125 |
-
|
| 126 |
-
## 3. Anotación Acelerada (SAM → COCO + YOLO)
|
| 127 |
-
|
| 128 |
-
**SAM (Segment Anything)** genera máscaras/cajas desde clics o cajas. Se exporta a **ambos formatos**: **COCO** (`_annotations.coco.json`, para RF-DETR) y **YOLO** (`.txt`, para YOLO26). Roboflow puede exportar a los dos. **DINOv3** ayuda con *pre-clustering* (features densas) en cultivos de geometría repetitiva (hileras de arándano).
|
| 129 |
-
|
| 130 |
-
> **Salida:** dataset anotado por cultivo, en COCO y YOLO, listo para entrenar cualquier candidato.
|
| 131 |
-
|
| 132 |
-
---
|
| 133 |
-
|
| 134 |
-
## 4. Candidatos de Modelo (se publica el mejor)
|
| 135 |
-
|
| 136 |
-
### 4.1 Candidato A — YOLO26 (Ultralytics, AGPL-3.0)
|
| 137 |
-
|
| 138 |
-
**YOLO26** (ene 2026): NMS-free end-to-end, unifica detect/seg/pose, **~43 % más rápido en CPU**, export ONNX. Variante **nano** (`yolo26n`) para CPU. **Habilitado** porque AgroVisión acepta **AGPL-3.0** (app open-source). [Docs](https://docs.ultralytics.com/models/yolo26)
|
| 139 |
-
|
| 140 |
-
### 4.2 Candidato B — RF-DETR (Roboflow, Apache 2.0)
|
| 141 |
-
|
| 142 |
-
**RF-DETR** (ICLR 2026): Detection Transformer real-time, **NMS-free**, **Apache 2.0** (permisivo), variante **Nano** para CPU, detect + **RF-DETR-Seg**. Alternativa si se quisiera evitar AGPL en el futuro. [GitHub](https://github.com/roboflow/rf-detr)
|
| 143 |
-
|
| 144 |
-
### 4.3 Track C — DINOv3 (dominio + satélite)
|
| 145 |
-
|
| 146 |
-
**DINOv3** (Meta, comercial-permisiva): backbone congelado SOTA sin fine-tuning + **backbone satelital MAXAR**. Robustez de dominio/estación y conteo de árboles/copa en satélite. [GitHub](https://github.com/facebookresearch/dinov3)
|
| 147 |
-
|
| 148 |
-
### 4.4 Baseline Satélite — DeepForest (MIT)
|
| 149 |
-
|
| 150 |
-
**DeepForest**: conteo de copas en RGB aéreo, modelo NEON preentrenado. Baseline del track satelital; reusa pesos publicados. [GitHub](https://github.com/weecology/DeepForest)
|
| 151 |
-
|
| 152 |
-
### 4.5 Tabla Comparativa
|
| 153 |
-
|
| 154 |
-
| Atributo | YOLO26-nano (A) | RF-DETR-Nano (B) | DINOv3 (C) | DeepForest |
|
| 155 |
-
| :--- | :--- | :--- | :--- | :--- |
|
| 156 |
-
| **Licencia** | AGPL-3.0 | Apache 2.0 | Comercial-permisiva | MIT |
|
| 157 |
-
| **Tarea** | detect/seg | detect/seg | backbone SSL | detect copas |
|
| 158 |
-
| **Post-proceso** | NMS-free | NMS-free | cabezal | RetinaNet |
|
| 159 |
-
| **Formato datos** | YOLO | COCO | — | — |
|
| 160 |
-
| **CPU/ONNX** | ✅ | ✅ | GPU | CPU/GPU |
|
| 161 |
-
| **Rol** | **Candidato (gana si menor MAE)** | **Candidato** | Dominio/satélite | Baseline satélite |
|
| 162 |
-
|
| 163 |
-
> **Criterio de selección:** se entrena cada candidato sobre el cultivo objetivo; gana el de **menor MAE de conteo** cumpliendo tamaño/latencia en CPU. La *model card* declara el ganador, su `architecture` y su licencia.
|
| 164 |
-
|
| 165 |
-
---
|
| 166 |
-
|
| 167 |
-
## 5. Entrenamiento, Hiperparámetros y Selección
|
| 168 |
-
|
| 169 |
-
### 5.1 Estrategia (notebooks-first, multi-candidato, multi-cultivo)
|
| 170 |
-
|
| 171 |
-
1. **PoC en notebooks** por cultivo (arándano primero): entrenar YOLO26 y RF-DETR cortos en Colab/Kaggle.
|
| 172 |
-
2. **Promoción a `.py`** con config declarativa y semillas fijas.
|
| 173 |
-
3. **Transfer learning** desde pesos COCO de cada candidato; *tiling* por GSD + augmentations.
|
| 174 |
-
4. **Selección**: comparar candidatos por MAE/tamaño/latencia → publicar el mejor.
|
| 175 |
-
|
| 176 |
-
### 5.2 Configuración de Referencia
|
| 177 |
-
|
| 178 |
-
```yaml
|
| 179 |
-
# configs/train.yaml (referencia)
|
| 180 |
-
crop: blueberry # cultivo objetivo (multi-cultivo)
|
| 181 |
-
candidates: [yolo26n, rfdetr_nano]
|
| 182 |
-
classes: ["arbusto"] # arándano: 1 clase (arbusto). Multi-clase si aplica
|
| 183 |
-
imgsz: 640 # YOLO26; RF-DETR usa múltiplos de 56 (p. ej. 560)
|
| 184 |
-
epochs: 100
|
| 185 |
-
seed: 42
|
| 186 |
-
```
|
| 187 |
-
|
| 188 |
-
### 5.3 Métricas y Selección
|
| 189 |
-
* **Detección:** Precision, Recall, mAP@0.5, mAP@0.5-0.95, F1.
|
| 190 |
-
* **Conteo (negocio):** **MAE/RMSE** entre conteo predicho y real → **criterio de selección**.
|
| 191 |
-
* **Por cultivo:** métricas reportadas por cultivo en `metrics.json`.
|
| 192 |
-
|
| 193 |
-
---
|
| 194 |
-
|
| 195 |
-
## 6. Contrato de Inferencia y Publicación (Handoff a AgroVisión)
|
| 196 |
-
|
| 197 |
-
### 6.1 Exportación a ONNX (según el candidato ganador)
|
| 198 |
-
|
| 199 |
-
```bash
|
| 200 |
-
# YOLO26 (si gana)
|
| 201 |
-
uv run yolo detect export model=runs/.../best.pt format=onnx opset=12 simplify=True
|
| 202 |
-
# RF-DETR (si gana)
|
| 203 |
-
uv pip install "rfdetr[onnx]" && python -c "from rfdetr import RFDETRNano; RFDETRNano(pretrain_weights='output/checkpoint_best.pth').export(format='onnx')"
|
| 204 |
-
# renombrar a marca: agrovision-plantcount-vX.Y.Z.onnx
|
| 205 |
-
```
|
| 206 |
-
|
| 207 |
-
### 6.2 Publicación en Hugging Face Hub (1 artefacto)
|
| 208 |
-
|
| 209 |
-
```python
|
| 210 |
-
from huggingface_hub import HfApi
|
| 211 |
-
HfApi().upload_file(path_or_fileobj="models/agrovision-plantcount-v2.0.0.onnx",
|
| 212 |
-
path_in_repo="agrovision-plantcount-v2.0.0.onnx",
|
| 213 |
-
repo_id="<org>/agrovision-plantcount", repo_type="model")
|
| 214 |
-
# + README.md (model card: arquitectura ganadora, licencia, cultivo, métricas)
|
| 215 |
-
```
|
| 216 |
-
|
| 217 |
-
### 6.3 Contrato de Inferencia (estable, agnóstico a la arquitectura)
|
| 218 |
-
|
| 219 |
-
```json
|
| 220 |
-
{
|
| 221 |
-
"boxes": [[x1, y1, x2, y2, conf, cls]],
|
| 222 |
-
"count": 124,
|
| 223 |
-
"classes": {"0": "arbusto"},
|
| 224 |
-
"crop": "blueberry",
|
| 225 |
-
"model_version": "2.0.0",
|
| 226 |
-
"architecture": "yolo26n", // o "rfdetr_nano" — el ganador
|
| 227 |
-
"license": "AGPL-3.0", // o "Apache-2.0"
|
| 228 |
-
"nms_free": true
|
| 229 |
-
}
|
| 230 |
-
```
|
| 231 |
-
|
| 232 |
-
* **Agnóstico:** AgroVisión lee `architecture` y aplica el **adaptador de decode** correspondiente; el nombre del artefacto es de marca (`agrovision-plantcount`), no cambia al cambiar de modelo.
|
| 233 |
-
* **Multi-cultivo:** `crop` y `classes` describen el cultivo/clases del modelo publicado.
|
| 234 |
-
|
| 235 |
-
### 6.4 Consumo en AgroVisión (build de Docker)
|
| 236 |
-
|
| 237 |
-
```dockerfile
|
| 238 |
-
RUN uv run python -c "from huggingface_hub import hf_hub_download; \
|
| 239 |
-
hf_hub_download(repo_id='<org>/agrovision-plantcount', \
|
| 240 |
-
filename='agrovision-plantcount-v2.0.0.onnx', local_dir='/app/models')"
|
| 241 |
-
ENV MODEL_PATH=/app/models/agrovision-plantcount-v2.0.0.onnx
|
| 242 |
-
ENV MODEL_VERSION=2.0.0
|
| 243 |
-
```
|
| 244 |
-
|
| 245 |
-
---
|
| 246 |
-
|
| 247 |
-
## 7. Estructura del Repo, Docker y Reproducibilidad
|
| 248 |
-
|
| 249 |
-
```
|
| 250 |
-
agrovision-plant-count-model/
|
| 251 |
-
├── notebooks/ # 01_explore · 02_annotate_sam · 03_train_candidates · 04_satellite
|
| 252 |
-
├── src/
|
| 253 |
-
│ ├── ingest.py # descarga por cultivo (COCO + YOLO)
|
| 254 |
-
│ ├── annotate_sam.py # SAM → COCO + YOLO
|
| 255 |
-
�� ├── preprocess.py # tiling/splits/augment + clases por cultivo
|
| 256 |
-
│ ├── train.py # entrena candidatos (yolo26 / rfdetr)
|
| 257 |
-
│ ├── evaluate.py # métricas + SELECCIÓN del mejor
|
| 258 |
-
│ ├── export.py # ganador → ONNX + model card
|
| 259 |
-
│ ├── publish.py # → Hugging Face Hub
|
| 260 |
-
│ └── satellite/ # deepforest_count.py · dinov3_features.py
|
| 261 |
-
├── configs/ # train.yaml (crop, candidates, classes)
|
| 262 |
-
├── data/ # raw/<cultivo>/ · processed/<cultivo>/ (NO commitear)
|
| 263 |
-
├── models/ # .onnx + metrics.json
|
| 264 |
-
├── tests/ # test_contract · test_preprocess · test_parity
|
| 265 |
-
├── Dockerfile · pyproject.toml · .gitignore · README.md
|
| 266 |
-
```
|
| 267 |
-
|
| 268 |
-
### 7.1 Dependencias
|
| 269 |
-
```toml
|
| 270 |
-
"ultralytics>=8.3", # YOLO26 (AGPL — uso aceptado)
|
| 271 |
-
"rfdetr[onnx]>=1.4", # RF-DETR (Apache 2.0)
|
| 272 |
-
"torch>=2.2", "torchvision>=0.17",
|
| 273 |
-
"onnx>=1.16", "onnxruntime>=1.18",
|
| 274 |
-
"opencv-python-headless>=4.9", "numpy>=2.1",
|
| 275 |
-
"albumentations>=1.4", "supervision>=0.20",
|
| 276 |
-
"segment-anything-2", # SAM (anotación)
|
| 277 |
-
"deepforest>=1.4", # baseline árboles (MIT)
|
| 278 |
-
"kagglehub", "roboflow", # ingesta (COCO + YOLO)
|
| 279 |
-
"huggingface_hub", # publicación
|
| 280 |
-
# DINOv3 vía torch.hub / facebookresearch/dinov3
|
| 281 |
-
```
|
| 282 |
-
> **Python ≥ 3.10** (requisito de `rfdetr`) y **`uv`**. Entrenamiento en Colab/Kaggle (GPU T4 gratis); CPU local para preproceso/validación.
|
| 283 |
-
|
| 284 |
-
---
|
| 285 |
-
|
| 286 |
-
## 8. Reglas de Validación y Pruebas
|
| 287 |
-
1. **Contrato:** `tests/test_contract.py` valida el JSON §6.3 (incl. `architecture`, `crop`, `license`, `nms_free`).
|
| 288 |
-
2. **Sanity de conteo:** MAE bajo umbral por cultivo.
|
| 289 |
-
3. **Determinismo:** seeds fijas → reproducible.
|
| 290 |
-
4. **Selección:** se documenta el candidato ganador y por qué.
|
| 291 |
-
5. **Paridad notebook↔script** y **publicación** descargable de HF Hub.
|
| 292 |
-
|
| 293 |
-
---
|
| 294 |
-
|
| 295 |
-
## Apéndice — Fuentes
|
| 296 |
-
|
| 297 |
-
YOLO26 ([Ultralytics](https://docs.ultralytics.com/models/yolo26)); RF-DETR ([GitHub](https://github.com/roboflow/rf-detr)); DINOv3 ([Meta](https://ai.meta.com/blog/dinov3-self-supervised-vision-model/)); DeepForest ([GitHub](https://github.com/weecology/DeepForest)); **arándano con dron** ([arXiv 2501.02344](https://arxiv.org/abs/2501.02344)); cultivos UAV ([bioRxiv 2021](https://www.biorxiv.org/content/10.1101/2021.04.27.441631v2.full)); CitDet ([arXiv 2309.05645](https://arxiv.org/html/2309.05645v3)); TasselNetV4 ([arXiv 2509.20857](https://arxiv.org/pdf/2509.20857)); Hugging Face Hub ([docs](https://huggingface.co/docs/hub/en/models-downloading)).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|