Metadata-Version: 2.4
Name: shepherd-wasm
Version: 1.1.0
Summary: Shepherd segmentation (Remote Sensing 2019) in pure NumPy/SciPy - runs in Pyodide/WebAssembly and CPython. Bit-exact compat mode vs pyshepseg; algorithm-faithful by default.
Author: Abel Alejandro Coronado Iruegas (abxda), Claude (Anthropic)
License: MIT License
        
        Copyright (c) 2026 Abel Coronado (abxda) & Claude (Anthropic) — pure-NumPy port
        Copyright (c) 2021 Neil Flood and Sam Gillingham — original pyshepseg implementation
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/abxda/shepherd-wasm
Project-URL: Demo, https://abxda.github.io/portable-satelital/lab/index.html?path=Taller_ML_Urbano_WASM.ipynb
Project-URL: Issues, https://github.com/abxda/shepherd-wasm/issues
Keywords: remote-sensing,segmentation,shepherd,geobia,sentinel-2,webassembly,pyodide,pyshepseg,satellite-imagery
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Topic :: Scientific/Engineering :: Image Processing
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: scipy
Requires-Dist: scikit-learn
Dynamic: license-file

# 🛰️ shepherd-wasm

**Segmentación Shepherd (Shepherd, Bunting & Dymond 2019) en NumPy/SciPy puro — corre en WebAssembly.**

*Pure NumPy/SciPy implementation of Shepherd segmentation for satellite imagery. Bit-exact
against `pyshepseg`, **numba-free**, so it runs in the browser via Pyodide/WebAssembly — and
just as happily in regular CPython.*

**🎬 Demo en vivo (todo corre en tu navegador):**
<https://abxda.github.io/portable-satelital/lab/index.html?path=Taller_ML_Urbano_WASM.ipynb>

---

## ¿Por qué existe esto?

La implementación de referencia, [`pyshepseg`](https://github.com/ubarsc/pyshepseg), acelera
sus bucles críticos con **numba** (JIT sobre LLVM). Eso la hace rápida — y también
**imposible de ejecutar en WebAssembly**: el sandbox WASM prohíbe la generación de código
nativo en tiempo de ejecución, y numba no existe (ni existirá) en Pyodide.

`shepherd_pure.py` es un **port fiel, línea por línea**, de `pyshepseg` 2.0.5 a
NumPy/SciPy vectorizado. Sin numba, sin C, sin compilación: un solo archivo que corre
idéntico en CPython y dentro de una pestaña del navegador.

## Fidelidad: bit-exacto, no "parecido"

Validado contra `pyshepseg` 2.0.5 sobre ventanas **reales** de una geomediana Sentinel-2
(12 bandas, int16, Aguascalientes, México), compartiendo el mismo objeto KMeans
(`kmeansObj`) para aislar los pasos reimplementados:

| Ventana | minSegmentSize | ARI | Píxeles idénticos | Segmentos (ref / puro) |
|---|---|---|---|---|
| 256×256 (centro) | 50 | **1.000000** | **100 %** | 587 / 587 |
| 512×512 (centro) | 50 | **1.000000** | **100 %** | 2 082 / 2 082 |
| 512×512 (borde, con nodata) | 50 | **1.000000** | **100 %** | 2 191 / 2 191 |
| 1024×1024 | 100 | **1.000000** | **100 %** | 4 700 / 4 700 |

Misma cuenta de píxeles sueltos eliminados, mismos segmentos pequeños fusionados, mismo
`maxSpectralDiff` automático. La fidelidad incluye los detalles que no aparecen en el
paper: IDs de clump por orden de descubrimiento en barrido raster, tope
`MAX_CLUMP_SIZE=10000`, la pasada previa de eliminación de píxeles sueltos, y los
desempates "primero encontrado" en orden fila→columna. Reproduce la validación con
[`validate_shepherd.py`](validate_shepherd.py).

## Rendimiento (sorpresa)

| Tile | pyshepseg (numba, incluye ~4 s de JIT) | shepherd-wasm (CPython) | shepherd-wasm (navegador, Pyodide) |
|---|---|---|---|
| 256² ×12 bandas | 4.5 s | 0.6 s | **0.8 s** |
| 512² ×12 | 4.4 s | 1.8 s | — |
| 1024² ×12 | 4.7 s | 17.5 s | — |

En tiles chicos gana al original (se ahorra el costo del JIT); en tiles grandes el
procesamiento por mosaico — el mismo patrón de `pyshepseg.tiling` — mantiene todo en la
zona dulce.

## Instalación

```bash
pip install shepherd-wasm
```

```python
import shepherd_wasm   # módulo canónico (import shepherd_pure sigue funcionando como alias)
```

## Uso

```python
import numpy as np
import shepherd_wasm

# img: ndarray (n_bandas, filas, columnas); usa float32 para evitar wraparound
res = shepherd_wasm.doShepherdSegmentation(
    img.astype(np.float32),
    numClusters=60,
    minSegmentSize=50,
    maxSpectralDiff='auto',
    imgNullVal=nodata,        # o None
    fixedKMeansInit=True,     # determinista (recomendado para docencia)
)
res.segimg                    # ndarray (filas, columnas) con IDs de segmento; 0 = nulo
res.singlePixelsEliminated, res.smallSegmentsEliminated, res.maxSpectralDiff
```

Misma firma y semántica que `pyshepseg.shepseg.doShepherdSegmentation` — es un reemplazo
directo. Dependencias: `numpy`, `scipy`, `scikit-learn` (las tres existen en Pyodide).

En **Pyodide/JupyterLite**, recuerda que el auto-cargador no ve los imports internos de un
módulo: ejecuta `import scipy.ndimage, sklearn.cluster` en una celda antes de
`import shepherd_pure`.

## Crédito y licencia

- Algoritmo: Shepherd, J.D.; Bunting, P.; Dymond, J.R. *Operational Large-Scale
  Segmentation of Imagery Based on Iterative Elimination.* Remote Sensing 2019, 11(6), 658.
  DOI [10.3390/rs11060658](https://doi.org/10.3390/rs11060658).
- Implementación de referencia: [`pyshepseg`](https://github.com/ubarsc/pyshepseg)
  © Neil Flood & Sam Gillingham (MIT). Este proyecto es un port derivado y conserva su
  licencia y crédito.
- Port a NumPy puro, validación y empaque WASM: **abxda × Claude** —
  [Dr. Abel Coronado](https://github.com/abxda) en colaboración con
  [Claude](https://claude.com) (Anthropic), 2026.

MIT. Ver [LICENSE](LICENSE).


## ⚠️ Divergencia documentada respecto a pyshepseg: `maxClumpSize`

`pyshepseg` introduce en su `clump()` un tope duro `MAX_CLUMP_SIZE = 10000`
píxeles (optimización del port a numba, **ausente en el paper y en RSGISLib**,
la implementación canónica de los mismos autores). Consecuencia: cualquier
objeto homogéneo mayor a 10,000 px — un lago, una presa, una mancha urbana —
queda **rebanado en tiras paralelas** (la huella del frente del flood fill)
que la eliminación iterativa jamás fusiona, porque cada tira supera
`minSegmentSize`.

**Caso real (Laguna de Yuriria, geomediana Sentinel-2 12 bandas):** con el
tope, el segmento mayor mide 11,767 px y el lago queda en ~30 tiras; sin el
tope, 387,989 px — el lago entero, estructuralmente idéntico al resultado de
RSGISLib.

Por eso esta biblioteca **diverge deliberadamente**:

| `maxClumpSize` | Comportamiento |
|---|---|
| `None` (**default**) | Fiel al **algoritmo** (paper / RSGISLib): objetos grandes enteros |
| `10000` | Modo **compatibilidad**: reproduce bit a bit a pyshepseg, incluido su artefacto (es lo que usa `validate_shepherd.py`) |

La validación es por lo tanto de dos niveles: *compatibilidad* (ARI 1.0
contra pyshepseg con `maxClumpSize=10000`) y *corrección* (objetos grandes
íntegros, consistente con RSGISLib).

