Skip to content

Commit 6589e05

Browse files
committed
Prepare 0.5.0 release
1 parent 3f908f2 commit 6589e05

24 files changed

Lines changed: 1114 additions & 128 deletions

.github/workflows/ci.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
check:
9+
runs-on: ubuntu-latest
10+
strategy:
11+
fail-fast: false
12+
matrix:
13+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
14+
steps:
15+
- uses: actions/checkout@v6
16+
with:
17+
fetch-depth: 0 # full history so setuptools-scm can read tags
18+
19+
- uses: actions/setup-python@v6
20+
with:
21+
python-version: ${{ matrix.python-version }}
22+
23+
- uses: astral-sh/setup-uv@v8.0.0
24+
with:
25+
cache-suffix: ${{ matrix.python-version }}
26+
27+
- run: uv sync --all-extras --dev
28+
29+
- name: Lint
30+
run: uv run ruff check .
31+
32+
- name: Type check
33+
run: uv run mypy omniwatermask/
34+
35+
- name: Test
36+
run: uv run pytest tests/ -x -q # e2e/inference tests excluded via addopts

.github/workflows/publish.yml

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
name: Publish to PyPI
2+
3+
# Triggered by pushing a tag that looks like a version, e.g. `v0.4.4`.
4+
# The version comes from the git tag itself (via setuptools-scm) — no file to bump.
5+
# Publishing uses PyPI trusted publishing (OIDC) — no API tokens, no secrets.
6+
# To enable: at https://pypi.org/manage/project/omniwatermask/settings/publishing/
7+
# add a publisher with:
8+
# Owner DPIRD-DMA
9+
# Repository name OmniWaterMask
10+
# Workflow name publish.yml
11+
# Environment pypi
12+
13+
on:
14+
push:
15+
tags:
16+
- "v[0-9]+.[0-9]+.[0-9]+*"
17+
18+
jobs:
19+
build:
20+
name: Build distribution
21+
runs-on: ubuntu-latest
22+
steps:
23+
- uses: actions/checkout@v6
24+
with:
25+
fetch-depth: 0 # full history so setuptools-scm can read tags
26+
27+
- uses: actions/setup-python@v6
28+
with:
29+
python-version: "3.11"
30+
31+
- uses: astral-sh/setup-uv@v8.0.0
32+
33+
- name: Build sdist + wheel
34+
run: uv build
35+
36+
- uses: actions/upload-artifact@v4
37+
with:
38+
name: python-package-distributions
39+
path: dist/
40+
41+
publish-to-pypi:
42+
name: Publish to PyPI
43+
needs: build
44+
runs-on: ubuntu-latest
45+
environment:
46+
name: pypi
47+
url: https://pypi.org/p/omniwatermask
48+
permissions:
49+
id-token: write # required for trusted publishing
50+
51+
steps:
52+
- uses: actions/download-artifact@v4
53+
with:
54+
name: python-package-distributions
55+
path: dist/
56+
57+
- uses: pypa/gh-action-pypi-publish@release/v1
58+
59+
github-release:
60+
name: Create GitHub Release
61+
needs: publish-to-pypi
62+
runs-on: ubuntu-latest
63+
permissions:
64+
contents: write # required to create releases
65+
66+
steps:
67+
- uses: actions/download-artifact@v4
68+
with:
69+
name: python-package-distributions
70+
path: dist/
71+
72+
- uses: softprops/action-gh-release@v2
73+
with:
74+
files: dist/*
75+
generate_release_notes: true

.gitignore

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
cache/*.json
2+
# coverage.py artifacts
3+
.coverage
4+
.coverage.*
5+
htmlcov/
6+
# setuptools-scm generated version file
7+
omniwatermask/_version.py
28
omniwatermask/__pycache__/*
39
omniwatermask/models/*.pth
410
water_vectors_cache/*
@@ -15,4 +21,9 @@ examples/data/naip_water_test_OmniWaterMask_0.4.3.tif
1521
examples/data/naip_water_test.tif
1622
examples/OWM_cache/*
1723
examples/cache/*
18-
examples/data/*
24+
examples/data/*
25+
data/S2B_MSIL2A_20241219T013659_R031_T52LEH_20241219T030742_TL_RGBNIR.tif
26+
OWM_cache/*
27+
data/S2A_56HLH_20231011_0_L2A_RGBNIR.tif
28+
examples/water_vectors_cache/gdfs/*.parquet
29+
examples/water_vectors_cache/*.db

.pre-commit-config.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
repos:
2+
- repo: https://github.com/astral-sh/ruff-pre-commit
3+
rev: v0.15.8
4+
hooks:
5+
- id: ruff-check
6+
- id: ruff-format
7+
- repo: local
8+
hooks:
9+
- id: mypy
10+
name: mypy
11+
entry: uv run mypy omniwatermask/
12+
language: system
13+
pass_filenames: false
14+
always_run: true
15+
- id: pytest
16+
name: pytest
17+
entry: uv run pytest tests/ -x -q -m "not e2e"
18+
language: system
19+
pass_filenames: false
20+
always_run: true
21+
stages: [pre-push]

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# Changelog
22

3+
## [0.5.0] - Jun 3, 2026
4+
5+
### Changed
6+
- **Breaking:** No-data is now written as a GDAL dataset mask via `dst.write_mask()` instead of as a second data band, so GIS software (e.g. QGIS) treats no-data pixels as transparent. The mask is embedded inside the GeoTIFF (`GDAL_TIFF_INTERNAL_MASK`) rather than written as a `.tif.msk` sidecar. Standard output GeoTIFFs now have a single `Water predictions` band; read the mask with `src.read_masks(1)`. Debug output is unchanged.
7+
- Versioning now derives from git tags via `setuptools-scm` (generates `omniwatermask/_version.py` at build time); the hardcoded `omniwatermask/__version__.py` was removed.
8+
- End-to-end tests are now excluded from the default test run (`addopts = "-m 'not e2e'"`); run them explicitly with `pytest -m e2e`.
9+
10+
### Added
11+
- `py.typed` marker and full type hints — the package now ships type information (PEP 561) and is checked with `mypy --strict`.
12+
- Pre-commit hooks (ruff lint/format, mypy, fast tests) and GitHub Actions CI.
13+
- PyPI trove classifiers and project URLs (Repository, Issues, Changelog) in `pyproject.toml`.
14+
- Cloudy Sentinel-2 example notebook demonstrating cloud masking with OmniCloudMask before inference, plus a "Cloudy imagery" guidance section in the README.
15+
316
## [0.4.3] - Mar 5, 2026
417

518
### Fixed

README.md

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
# OmniWaterMask
1+
<div align="center">
2+
3+
<img src="https://raw.githubusercontent.com/DPIRD-DMA/OmniWaterMask/main/assets/omniwatermask-title.svg" alt="OmniWaterMask" width="680">
24

35
[![image](https://img.shields.io/pypi/v/omniwatermask.svg)](https://pypi.python.org/pypi/omniwatermask)
46
[![image](https://static.pepy.tech/badge/omniwatermask)](https://pepy.tech/project/omniwatermask)
57
[![image](https://img.shields.io/conda/vn/conda-forge/omniwatermask.svg)](https://anaconda.org/conda-forge/omniwatermask)
6-
[![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/omniwatermask.svg)](https://anaconda.org/conda-forge/omniwatermask)
78
[![Conda Recipe](https://img.shields.io/badge/recipe-omniwatermask-green.svg)](https://github.com/conda-forge/omniwatermask-feedstock)
89

10+
</div>
11+
912
OmniWaterMask is a Python library for high accuracy water segmentation in high to moderate resolution satellite imagery, supporting a wide range of resolutions, sensors, and processing levels.
1013

11-
[The OmniWaterMask paper is now published 🎉](https://www.sciencedirect.com/science/article/pii/S0924271625002692)
14+
[Check out the paper here](https://www.sciencedirect.com/science/article/pii/S0924271625002692)
1215

1316

1417
## Features
@@ -95,6 +98,17 @@ water_mask_path = make_water_mask(
9598

9699
- When working with scenes containing no-data regions, explicitly set the 'no_data_value' parameter to ensure proper handling of these areas.
97100

101+
### Cloudy imagery
102+
103+
If you are working with cloudy imagery, either:
104+
105+
- use a **temporal mosaic** that is already cloud and cloud-shadow free (e.g. via [s2mosaic](https://github.com/DPIRD-DMA/s2mosaic) for Sentinel-2), or
106+
- apply a **high quality cloud and cloud shadow mask** and set those pixels to `0` (the `no_data_value`) before running OWM.
107+
108+
This matters because OWM optimises its detection thresholds both **locally** (per region/patch) and **globally** (across the whole scene). Cloud and cloud-shadow pixels are out-of-distribution and can skew those optimisations, so bad data in one part of a scene can degrade the water prediction in other, otherwise-clean parts. Masking those pixels to no-data removes them from the optimisation entirely.
109+
110+
[OmniCloudMask](https://github.com/DPIRD-DMA/OmniCloudMask) is a good choice for the masking step. See the [cloudy Sentinel-2 example](https://github.com/DPIRD-DMA/OmniWaterMask/blob/main/examples/Sentinel-2%20example.ipynb) for an end-to-end mask-then-infer workflow.
111+
98112

99113
## Parameters
100114

@@ -145,6 +159,7 @@ Example notebooks are available in the [examples/](https://github.com/DPIRD-DMA/
145159

146160
- [NAIP example](https://github.com/DPIRD-DMA/OmniWaterMask/blob/main/examples/NAIP%20example.ipynb) — Water segmentation on NAIP aerial imagery from HuggingFace
147161
- [Sentinel-2 example](https://github.com/DPIRD-DMA/OmniWaterMask/blob/main/examples/Sentinel%202%20example.ipynb) — Water segmentation on a Sentinel-2 mosaic using [s2mosaic](https://github.com/DPIRD-DMA/s2mosaic)
162+
- [Cloudy Sentinel-2 example](https://github.com/DPIRD-DMA/OmniWaterMask/blob/main/examples/Sentinel-2%20example.ipynb) — Masking clouds with [OmniCloudMask](https://github.com/DPIRD-DMA/OmniCloudMask) before running OWM on a cloudy AWS scene
148163

149164
## Changelog
150165

@@ -154,6 +169,48 @@ See [CHANGELOG.md](https://github.com/DPIRD-DMA/OmniWaterMask/blob/main/CHANGELO
154169

155170
Contributions are welcome! Please submit a pull request or open an issue to discuss any changes.
156171

172+
### Development setup
173+
174+
Clone the repository and install the dependencies (including the dev group) with [uv](https://docs.astral.sh/uv/):
175+
176+
```bash
177+
uv sync --all-extras --dev
178+
```
179+
180+
Optionally install the git hooks (ruff lint/format on commit, mypy + the fast tests on push):
181+
182+
```bash
183+
uv run pre-commit install
184+
uv run pre-commit install --hook-type pre-push
185+
```
186+
187+
### Running the tests
188+
189+
Tests use `pytest`. The fast suite (unit tests + model-mocked pipeline tests) runs in a few seconds and is what CI runs by default:
190+
191+
```bash
192+
uv run pytest # full fast suite
193+
uv run pytest tests/test_orchestration.py # one file
194+
uv run pytest -k make_water_mask # match by name
195+
```
196+
197+
End-to-end tests that download the real model weights and run inference on real imagery are marked `e2e` and excluded by default (see `addopts` in `pyproject.toml`). To run them explicitly:
198+
199+
```bash
200+
uv run pytest -m e2e # only the e2e/inference tests
201+
uv run pytest -m "" # everything, including e2e
202+
```
203+
204+
Lint, format and type-check:
205+
206+
```bash
207+
uv run ruff check .
208+
uv run ruff format .
209+
uv run mypy omniwatermask/
210+
```
211+
212+
For maintainers: pushing a version tag (e.g. `git tag v0.4.4 && git push --tags`) builds the package and publishes it to PyPI via GitHub Actions trusted publishing — no tokens required.
213+
157214
## License
158215

159216
This project is licensed under the MIT License

assets/omniwatermask-title.svg

Lines changed: 33 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)