opencropphenotyping 0.2.0__tar.gz → 0.3.0__tar.gz
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.
- {opencropphenotyping-0.2.0/src/opencropphenotyping.egg-info → opencropphenotyping-0.3.0}/PKG-INFO +149 -8
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/README.md +145 -7
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/pyproject.toml +6 -1
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping/indices.py +200 -24
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping/io.py +317 -1
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping/segmentation.py +27 -1
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0/src/opencropphenotyping.egg-info}/PKG-INFO +149 -8
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/tests/test_indices.py +103 -3
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/tests/test_io.py +279 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/tests/test_segmentation.py +34 -1
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/LICENSE +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/setup.cfg +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping/batch_processing.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping/cli.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping/coco.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping/evaluation.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping/pipeline.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping/statistics.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping/traits.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping/visualization.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping.egg-info/SOURCES.txt +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping.egg-info/dependency_links.txt +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping.egg-info/entry_points.txt +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping.egg-info/requires.txt +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/src/opencropphenotyping.egg-info/top_level.txt +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/tests/test_batch_processing.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/tests/test_cli.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/tests/test_coco.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/tests/test_evaluation.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/tests/test_pipeline.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/tests/test_statistics.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/tests/test_toy_dataset.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/tests/test_traits.py +0 -0
- {opencropphenotyping-0.2.0 → opencropphenotyping-0.3.0}/tests/test_visualization.py +0 -0
{opencropphenotyping-0.2.0/src/opencropphenotyping.egg-info → opencropphenotyping-0.3.0}/PKG-INFO
RENAMED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencropphenotyping
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Open-source tools for crop phenotyping using multispectral geospatial imagery
|
|
5
5
|
Author: Aurélien Peter
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
6
9
|
Requires-Python: >=3.11
|
|
7
10
|
Description-Content-Type: text/markdown
|
|
8
11
|
License-File: LICENSE
|
|
@@ -26,15 +29,23 @@ Dynamic: license-file
|
|
|
26
29
|
|
|
27
30
|
[](https://github.com/Aurelien-Peter/OpenCropPhenotyping/actions/workflows/ci.yml)
|
|
28
31
|
[](https://codecov.io/github/Aurelien-Peter/OpenCropPhenotyping)
|
|
32
|
+
[](https://pypi.org/project/opencropphenotyping/)
|
|
33
|
+
[](https://pypi.org/project/opencropphenotyping/)
|
|
34
|
+
[](https://github.com/Aurelien-Peter/OpenCropPhenotyping/actions/workflows/publish_docker.yml)
|
|
29
35
|
|
|
30
36
|
**An open-source Python framework for high-throughput crop phenotyping using drone and satellite imagery.**
|
|
31
37
|
|
|
32
38
|
## About the project
|
|
33
39
|
|
|
34
40
|
OpenCropPhenotyping is an open-source project dedicated to reproducible high-throughput crop phenotyping using remote sensing data.
|
|
41
|
+
|
|
35
42
|
The objective is to provide reproducible workflows to extract agronomic traits from UAV and Sentinel-2 imagery, from raw image preprocessing to trait extraction, statistical analysis and visualization.
|
|
43
|
+
|
|
44
|
+
|
|
36
45
|
The project currently combines multispectral Sentinel-2 processing with classical RGB UAV image analysis. The first UAV developments focus on crop-row detection, vegetation segmentation and plant-position analysis, with the objective of progressively extending the framework toward more advanced high-throughput phenotyping workflows.
|
|
37
46
|
|
|
47
|
+
The project includes automated testing, continuous integration, code coverage monitoring, and package distribution through PyPI. Docker support provides a reproducible runtime environment for command-line processing.
|
|
48
|
+
|
|
38
49
|
## Current Features:
|
|
39
50
|
**Sentinel-2**
|
|
40
51
|
- [X] Sentinel-2 imagery reading
|
|
@@ -51,13 +62,12 @@ The project currently combines multispectral Sentinel-2 processing with classica
|
|
|
51
62
|
- [X] Processing results export
|
|
52
63
|
- [X] Batch processing of multiple Sentinel-2 products
|
|
53
64
|
- [X] Visualization
|
|
54
|
-
- [X] Unit Tests
|
|
55
|
-
- [X] Command-Line Interface (CLI)
|
|
56
65
|
|
|
57
66
|
**UAV RGB**
|
|
58
67
|
- [X] RGB UAV imagery reading
|
|
59
68
|
- [X] RGB vegetation indices
|
|
60
69
|
- [X] Vegetation segmentation
|
|
70
|
+
- [X] Vegetation fraction estimation
|
|
61
71
|
- [X] Crop-row orientation estimation
|
|
62
72
|
- [X] Crop-row detection
|
|
63
73
|
- [X] Crop-row boundary detection
|
|
@@ -66,12 +76,22 @@ The project currently combines multispectral Sentinel-2 processing with classica
|
|
|
66
76
|
- [X] Georeferenced UAV Image support
|
|
67
77
|
- [X] Experimental plot metadata integration
|
|
68
78
|
- [X] UAV RGB workflow demonstration
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
- [
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
- [x] Georeferenced raster metadata validation
|
|
80
|
+
- [x] GeoTIFF raster tiling with geospatial metadata preservation
|
|
81
|
+
- [x] Reconstruction of raster mosaics from tiles
|
|
82
|
+
|
|
83
|
+
**Software quality & distribution**
|
|
84
|
+
|
|
85
|
+
- [X] Unit and integration tests with pytest
|
|
86
|
+
- [X] Automated code quality checks with Ruff
|
|
87
|
+
- [X] Automated CI with GitHub Actions
|
|
88
|
+
- [X] Test coverage monitoring with pytest-cov and Codecov
|
|
89
|
+
- [X] Python package build and distribution through PyPI
|
|
90
|
+
- [X] Docker containerization
|
|
91
|
+
- [X] Automated Docker image publishing through GitHub Actions
|
|
92
|
+
- [X] Dependency update management with Dependabot
|
|
74
93
|
- [X] Reproducible toy datasets
|
|
94
|
+
- [X] Command-line interface (CLI)
|
|
75
95
|
|
|
76
96
|
## Case studies
|
|
77
97
|
|
|
@@ -676,6 +696,127 @@ opencropphenotyping batch-uav \
|
|
|
676
696
|
|
|
677
697
|
The CLI follows the same processing pipeline as the Python API and can therefore be used for reproducible processing of individual UAV datasets or complete directories of datasets.
|
|
678
698
|
|
|
699
|
+
## Georeferenced raster support and tiling
|
|
700
|
+
|
|
701
|
+
OpenCropPhenotyping provides utilities to validate georeferenced raster metadata, split GeoTIFF rasters into smaller tiles, and reconstruct a raster mosaic from these tiles.
|
|
702
|
+
|
|
703
|
+
These utilities support the handling of large UAV orthomosaics while preserving their spatial reference information.
|
|
704
|
+
|
|
705
|
+
### Available functions
|
|
706
|
+
|
|
707
|
+
- `validate_georeferenced_raster()`: checks the consistency of raster georeferencing metadata.
|
|
708
|
+
- `tile_raster()`: divides a raster into smaller tiles while preserving their spatial metadata.
|
|
709
|
+
- `merge_raster_tiles()`: reconstructs a raster mosaic from georeferenced tiles.
|
|
710
|
+
|
|
711
|
+
The functions operate on existing georeferenced rasters. They do not generate orthomosaics from raw UAV photographs, which requires a dedicated photogrammetry workflow.
|
|
712
|
+
|
|
713
|
+
## Docker
|
|
714
|
+
|
|
715
|
+
OpenCropPhenotyping is available as a Docker image, providing a reproducible Linux environment with the package and its dependencies pre-installed.
|
|
716
|
+
|
|
717
|
+
Docker images are published to GitHub Container Registry (GHCR) when a new version is released.
|
|
718
|
+
|
|
719
|
+
### Requirements
|
|
720
|
+
|
|
721
|
+
- [Docker Desktop](https://www.docker.com/products/docker-desktop/) or Docker Engine
|
|
722
|
+
- UAV or Sentinel-2 input data (toy datasets are included in the image)
|
|
723
|
+
- A published OpenCropPhenotyping Docker image
|
|
724
|
+
|
|
725
|
+
### Download the image
|
|
726
|
+
|
|
727
|
+
Pull a specific released version:
|
|
728
|
+
|
|
729
|
+
```bash
|
|
730
|
+
docker pull ghcr.io/aurelien-peter/opencropphenotyping:VERSION
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
Replace `VERSION` with an available release tag, such as `0.2.2`.
|
|
734
|
+
|
|
735
|
+
### Check the installation
|
|
736
|
+
|
|
737
|
+
Display the available CLI commands:
|
|
738
|
+
|
|
739
|
+
```bash
|
|
740
|
+
docker run --rm ghcr.io/aurelien-peter/opencropphenotyping:VERSION --help
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
Available commands include:
|
|
744
|
+
|
|
745
|
+
- `process`: process a single Sentinel-2 product.
|
|
746
|
+
- `batch`: process multiple Sentinel-2 products.
|
|
747
|
+
- `segment-uav`: process a single UAV RGB dataset.
|
|
748
|
+
- `batch-uav`: process multiple UAV datasets.
|
|
749
|
+
|
|
750
|
+
### Process Sentinel-2 imagery
|
|
751
|
+
|
|
752
|
+
Input data and output directories are provided through a bind mount. In the following example, a local `data` directory is mounted at `/data` inside the container.
|
|
753
|
+
|
|
754
|
+
```bash
|
|
755
|
+
docker run --rm \
|
|
756
|
+
--mount type=bind,source=/absolute/path/to/data,target=/data \
|
|
757
|
+
ghcr.io/aurelien-peter/opencropphenotyping:VERSION \
|
|
758
|
+
process \
|
|
759
|
+
--input-dir /data/raw/sentinel_product \
|
|
760
|
+
--output-dir /data/results \
|
|
761
|
+
--processed-dir /data/processed \
|
|
762
|
+
--indices ndvi \
|
|
763
|
+
--ndvi-threshold 0.3 \
|
|
764
|
+
--resolution 10
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
Replace `/absolute/path/to/data` with the absolute path to the local directory containing your data.
|
|
768
|
+
|
|
769
|
+
The input path must point to a Sentinel-2 product supported by the pipeline. Results written under `/data` are available in the corresponding local directory after the container exits.
|
|
770
|
+
|
|
771
|
+
### Process UAV RGB imagery
|
|
772
|
+
|
|
773
|
+
A single UAV dataset requires an RGB image, its georeferenced GeoTIFF and a GeoPackage describing the experimental plot.
|
|
774
|
+
|
|
775
|
+
```bash
|
|
776
|
+
docker run --rm \
|
|
777
|
+
--mount type=bind,source=/absolute/path/to/data,target=/data \
|
|
778
|
+
ghcr.io/aurelien-peter/opencropphenotyping:VERSION \
|
|
779
|
+
segment-uav \
|
|
780
|
+
--image /data/uav/image.png \
|
|
781
|
+
--geotiff /data/uav/image_georef.tif \
|
|
782
|
+
--geopackage /data/uav/plots.gpkg \
|
|
783
|
+
--output-dir /data/results/uav
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
To process several UAV datasets, use the `batch-uav` command. Each dataset must be stored in its own subdirectory containing the required input files.
|
|
787
|
+
|
|
788
|
+
```bash
|
|
789
|
+
docker run --rm \
|
|
790
|
+
--mount type=bind,source=/absolute/path/to/data,target=/data \
|
|
791
|
+
ghcr.io/aurelien-peter/opencropphenotyping:VERSION \
|
|
792
|
+
batch-uav \
|
|
793
|
+
--input-dir /data/uav \
|
|
794
|
+
--output-dir /data/results/uav
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
### Build the image locally
|
|
798
|
+
|
|
799
|
+
To build the Docker image from the source code instead of pulling a published release, clone the repository and run:
|
|
800
|
+
|
|
801
|
+
```bash
|
|
802
|
+
git clone https://github.com/Aurelien-Peter/OpenCropPhenotyping.git
|
|
803
|
+
cd OpenCropPhenotyping
|
|
804
|
+
|
|
805
|
+
docker build -t opencropphenotyping:local .
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
Check the local image:
|
|
809
|
+
|
|
810
|
+
```bash
|
|
811
|
+
docker run --rm opencropphenotyping:local --help
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
### Notes
|
|
815
|
+
|
|
816
|
+
- The container is designed for command-line processing jobs. It starts, executes the requested command and exits when the job is complete.
|
|
817
|
+
- Use a bind mount for both input data and output directories to keep files accessible outside the container.
|
|
818
|
+
|
|
819
|
+
|
|
679
820
|
## Contributing
|
|
680
821
|
|
|
681
822
|
Contributions are welcome. If you have ideas for improvements, bug fixes or new features, feel free to open an issue or submit a pull request.
|
|
@@ -2,15 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/Aurelien-Peter/OpenCropPhenotyping/actions/workflows/ci.yml)
|
|
4
4
|
[](https://codecov.io/github/Aurelien-Peter/OpenCropPhenotyping)
|
|
5
|
+
[](https://pypi.org/project/opencropphenotyping/)
|
|
6
|
+
[](https://pypi.org/project/opencropphenotyping/)
|
|
7
|
+
[](https://github.com/Aurelien-Peter/OpenCropPhenotyping/actions/workflows/publish_docker.yml)
|
|
5
8
|
|
|
6
9
|
**An open-source Python framework for high-throughput crop phenotyping using drone and satellite imagery.**
|
|
7
10
|
|
|
8
11
|
## About the project
|
|
9
12
|
|
|
10
13
|
OpenCropPhenotyping is an open-source project dedicated to reproducible high-throughput crop phenotyping using remote sensing data.
|
|
14
|
+
|
|
11
15
|
The objective is to provide reproducible workflows to extract agronomic traits from UAV and Sentinel-2 imagery, from raw image preprocessing to trait extraction, statistical analysis and visualization.
|
|
16
|
+
|
|
17
|
+
|
|
12
18
|
The project currently combines multispectral Sentinel-2 processing with classical RGB UAV image analysis. The first UAV developments focus on crop-row detection, vegetation segmentation and plant-position analysis, with the objective of progressively extending the framework toward more advanced high-throughput phenotyping workflows.
|
|
13
19
|
|
|
20
|
+
The project includes automated testing, continuous integration, code coverage monitoring, and package distribution through PyPI. Docker support provides a reproducible runtime environment for command-line processing.
|
|
21
|
+
|
|
14
22
|
## Current Features:
|
|
15
23
|
**Sentinel-2**
|
|
16
24
|
- [X] Sentinel-2 imagery reading
|
|
@@ -27,13 +35,12 @@ The project currently combines multispectral Sentinel-2 processing with classica
|
|
|
27
35
|
- [X] Processing results export
|
|
28
36
|
- [X] Batch processing of multiple Sentinel-2 products
|
|
29
37
|
- [X] Visualization
|
|
30
|
-
- [X] Unit Tests
|
|
31
|
-
- [X] Command-Line Interface (CLI)
|
|
32
38
|
|
|
33
39
|
**UAV RGB**
|
|
34
40
|
- [X] RGB UAV imagery reading
|
|
35
41
|
- [X] RGB vegetation indices
|
|
36
42
|
- [X] Vegetation segmentation
|
|
43
|
+
- [X] Vegetation fraction estimation
|
|
37
44
|
- [X] Crop-row orientation estimation
|
|
38
45
|
- [X] Crop-row detection
|
|
39
46
|
- [X] Crop-row boundary detection
|
|
@@ -42,12 +49,22 @@ The project currently combines multispectral Sentinel-2 processing with classica
|
|
|
42
49
|
- [X] Georeferenced UAV Image support
|
|
43
50
|
- [X] Experimental plot metadata integration
|
|
44
51
|
- [X] UAV RGB workflow demonstration
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
- [
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
- [x] Georeferenced raster metadata validation
|
|
53
|
+
- [x] GeoTIFF raster tiling with geospatial metadata preservation
|
|
54
|
+
- [x] Reconstruction of raster mosaics from tiles
|
|
55
|
+
|
|
56
|
+
**Software quality & distribution**
|
|
57
|
+
|
|
58
|
+
- [X] Unit and integration tests with pytest
|
|
59
|
+
- [X] Automated code quality checks with Ruff
|
|
60
|
+
- [X] Automated CI with GitHub Actions
|
|
61
|
+
- [X] Test coverage monitoring with pytest-cov and Codecov
|
|
62
|
+
- [X] Python package build and distribution through PyPI
|
|
63
|
+
- [X] Docker containerization
|
|
64
|
+
- [X] Automated Docker image publishing through GitHub Actions
|
|
65
|
+
- [X] Dependency update management with Dependabot
|
|
50
66
|
- [X] Reproducible toy datasets
|
|
67
|
+
- [X] Command-line interface (CLI)
|
|
51
68
|
|
|
52
69
|
## Case studies
|
|
53
70
|
|
|
@@ -652,6 +669,127 @@ opencropphenotyping batch-uav \
|
|
|
652
669
|
|
|
653
670
|
The CLI follows the same processing pipeline as the Python API and can therefore be used for reproducible processing of individual UAV datasets or complete directories of datasets.
|
|
654
671
|
|
|
672
|
+
## Georeferenced raster support and tiling
|
|
673
|
+
|
|
674
|
+
OpenCropPhenotyping provides utilities to validate georeferenced raster metadata, split GeoTIFF rasters into smaller tiles, and reconstruct a raster mosaic from these tiles.
|
|
675
|
+
|
|
676
|
+
These utilities support the handling of large UAV orthomosaics while preserving their spatial reference information.
|
|
677
|
+
|
|
678
|
+
### Available functions
|
|
679
|
+
|
|
680
|
+
- `validate_georeferenced_raster()`: checks the consistency of raster georeferencing metadata.
|
|
681
|
+
- `tile_raster()`: divides a raster into smaller tiles while preserving their spatial metadata.
|
|
682
|
+
- `merge_raster_tiles()`: reconstructs a raster mosaic from georeferenced tiles.
|
|
683
|
+
|
|
684
|
+
The functions operate on existing georeferenced rasters. They do not generate orthomosaics from raw UAV photographs, which requires a dedicated photogrammetry workflow.
|
|
685
|
+
|
|
686
|
+
## Docker
|
|
687
|
+
|
|
688
|
+
OpenCropPhenotyping is available as a Docker image, providing a reproducible Linux environment with the package and its dependencies pre-installed.
|
|
689
|
+
|
|
690
|
+
Docker images are published to GitHub Container Registry (GHCR) when a new version is released.
|
|
691
|
+
|
|
692
|
+
### Requirements
|
|
693
|
+
|
|
694
|
+
- [Docker Desktop](https://www.docker.com/products/docker-desktop/) or Docker Engine
|
|
695
|
+
- UAV or Sentinel-2 input data (toy datasets are included in the image)
|
|
696
|
+
- A published OpenCropPhenotyping Docker image
|
|
697
|
+
|
|
698
|
+
### Download the image
|
|
699
|
+
|
|
700
|
+
Pull a specific released version:
|
|
701
|
+
|
|
702
|
+
```bash
|
|
703
|
+
docker pull ghcr.io/aurelien-peter/opencropphenotyping:VERSION
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
Replace `VERSION` with an available release tag, such as `0.2.2`.
|
|
707
|
+
|
|
708
|
+
### Check the installation
|
|
709
|
+
|
|
710
|
+
Display the available CLI commands:
|
|
711
|
+
|
|
712
|
+
```bash
|
|
713
|
+
docker run --rm ghcr.io/aurelien-peter/opencropphenotyping:VERSION --help
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
Available commands include:
|
|
717
|
+
|
|
718
|
+
- `process`: process a single Sentinel-2 product.
|
|
719
|
+
- `batch`: process multiple Sentinel-2 products.
|
|
720
|
+
- `segment-uav`: process a single UAV RGB dataset.
|
|
721
|
+
- `batch-uav`: process multiple UAV datasets.
|
|
722
|
+
|
|
723
|
+
### Process Sentinel-2 imagery
|
|
724
|
+
|
|
725
|
+
Input data and output directories are provided through a bind mount. In the following example, a local `data` directory is mounted at `/data` inside the container.
|
|
726
|
+
|
|
727
|
+
```bash
|
|
728
|
+
docker run --rm \
|
|
729
|
+
--mount type=bind,source=/absolute/path/to/data,target=/data \
|
|
730
|
+
ghcr.io/aurelien-peter/opencropphenotyping:VERSION \
|
|
731
|
+
process \
|
|
732
|
+
--input-dir /data/raw/sentinel_product \
|
|
733
|
+
--output-dir /data/results \
|
|
734
|
+
--processed-dir /data/processed \
|
|
735
|
+
--indices ndvi \
|
|
736
|
+
--ndvi-threshold 0.3 \
|
|
737
|
+
--resolution 10
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
Replace `/absolute/path/to/data` with the absolute path to the local directory containing your data.
|
|
741
|
+
|
|
742
|
+
The input path must point to a Sentinel-2 product supported by the pipeline. Results written under `/data` are available in the corresponding local directory after the container exits.
|
|
743
|
+
|
|
744
|
+
### Process UAV RGB imagery
|
|
745
|
+
|
|
746
|
+
A single UAV dataset requires an RGB image, its georeferenced GeoTIFF and a GeoPackage describing the experimental plot.
|
|
747
|
+
|
|
748
|
+
```bash
|
|
749
|
+
docker run --rm \
|
|
750
|
+
--mount type=bind,source=/absolute/path/to/data,target=/data \
|
|
751
|
+
ghcr.io/aurelien-peter/opencropphenotyping:VERSION \
|
|
752
|
+
segment-uav \
|
|
753
|
+
--image /data/uav/image.png \
|
|
754
|
+
--geotiff /data/uav/image_georef.tif \
|
|
755
|
+
--geopackage /data/uav/plots.gpkg \
|
|
756
|
+
--output-dir /data/results/uav
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
To process several UAV datasets, use the `batch-uav` command. Each dataset must be stored in its own subdirectory containing the required input files.
|
|
760
|
+
|
|
761
|
+
```bash
|
|
762
|
+
docker run --rm \
|
|
763
|
+
--mount type=bind,source=/absolute/path/to/data,target=/data \
|
|
764
|
+
ghcr.io/aurelien-peter/opencropphenotyping:VERSION \
|
|
765
|
+
batch-uav \
|
|
766
|
+
--input-dir /data/uav \
|
|
767
|
+
--output-dir /data/results/uav
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### Build the image locally
|
|
771
|
+
|
|
772
|
+
To build the Docker image from the source code instead of pulling a published release, clone the repository and run:
|
|
773
|
+
|
|
774
|
+
```bash
|
|
775
|
+
git clone https://github.com/Aurelien-Peter/OpenCropPhenotyping.git
|
|
776
|
+
cd OpenCropPhenotyping
|
|
777
|
+
|
|
778
|
+
docker build -t opencropphenotyping:local .
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
Check the local image:
|
|
782
|
+
|
|
783
|
+
```bash
|
|
784
|
+
docker run --rm opencropphenotyping:local --help
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
### Notes
|
|
788
|
+
|
|
789
|
+
- The container is designed for command-line processing jobs. It starts, executes the requested command and exits when the job is complete.
|
|
790
|
+
- Use a bind mount for both input data and output directories to keep files accessible outside the container.
|
|
791
|
+
|
|
792
|
+
|
|
655
793
|
## Contributing
|
|
656
794
|
|
|
657
795
|
Contributions are welcome. If you have ideas for improvements, bug fixes or new features, feel free to open an issue or submit a pull request.
|
|
@@ -5,13 +5,18 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "opencropphenotyping"
|
|
8
|
-
version = "0.
|
|
8
|
+
version = "0.3.0"
|
|
9
9
|
description = "Open-source tools for crop phenotyping using multispectral geospatial imagery"
|
|
10
10
|
authors = [
|
|
11
11
|
{name = "Aurélien Peter"}
|
|
12
12
|
]
|
|
13
13
|
readme = "README.md"
|
|
14
14
|
requires-python = ">=3.11"
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
]
|
|
15
20
|
|
|
16
21
|
dependencies = [
|
|
17
22
|
"numpy",
|
|
@@ -144,7 +144,166 @@ def compute_savi(red_band: np.ndarray, nir_band: np.ndarray, L_factor : float =
|
|
|
144
144
|
|
|
145
145
|
return savi.astype(np.float32)
|
|
146
146
|
|
|
147
|
+
def compute_exg(
|
|
148
|
+
rgb: tuple[np.ndarray, np.ndarray, np.ndarray],
|
|
149
|
+
) -> np.ndarray:
|
|
150
|
+
"""Compute the Excess Green Index (ExG).
|
|
151
|
+
|
|
152
|
+
Parameters
|
|
153
|
+
----------
|
|
154
|
+
rgb : tuple[np.ndarray, np.ndarray, np.ndarray]
|
|
155
|
+
RGB image channels as (red, green, blue).
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
np.ndarray
|
|
160
|
+
ExG image with dtype float32.
|
|
161
|
+
"""
|
|
162
|
+
red, green, blue = (band.astype(np.float32) for band in rgb)
|
|
163
|
+
|
|
164
|
+
return 2 * green - red - blue
|
|
165
|
+
|
|
166
|
+
def compute_exr(
|
|
167
|
+
rgb: tuple[np.ndarray, np.ndarray, np.ndarray],
|
|
168
|
+
) -> np.ndarray:
|
|
169
|
+
"""Compute the Excess Red Index (ExR).
|
|
170
|
+
|
|
171
|
+
Parameters
|
|
172
|
+
----------
|
|
173
|
+
rgb : tuple[np.ndarray, np.ndarray, np.ndarray]
|
|
174
|
+
RGB image channels as (red, green, blue).
|
|
175
|
+
|
|
176
|
+
Returns
|
|
177
|
+
-------
|
|
178
|
+
np.ndarray
|
|
179
|
+
ExR image with dtype float32.
|
|
180
|
+
"""
|
|
181
|
+
red, green, _ = (band.astype(np.float32) for band in rgb)
|
|
182
|
+
|
|
183
|
+
return 1.4 * red - green
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def compute_exgr(
|
|
187
|
+
rgb: tuple[np.ndarray, np.ndarray, np.ndarray],
|
|
188
|
+
) -> np.ndarray:
|
|
189
|
+
"""Compute the Excess Green minus Excess Red Index (ExGR).
|
|
190
|
+
|
|
191
|
+
Parameters
|
|
192
|
+
----------
|
|
193
|
+
rgb : tuple[np.ndarray, np.ndarray, np.ndarray]
|
|
194
|
+
RGB image channels as (red, green, blue).
|
|
195
|
+
|
|
196
|
+
Returns
|
|
197
|
+
-------
|
|
198
|
+
np.ndarray
|
|
199
|
+
ExGR image with dtype float32.
|
|
200
|
+
"""
|
|
201
|
+
exg = compute_exg(rgb)
|
|
202
|
+
exr = compute_exr(rgb)
|
|
203
|
+
|
|
204
|
+
return exg - exr
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def compute_vari(
|
|
208
|
+
rgb: tuple[np.ndarray, np.ndarray, np.ndarray],
|
|
209
|
+
) -> np.ndarray:
|
|
210
|
+
"""Compute the Visible Atmospherically Resistant Index (VARI).
|
|
211
|
+
|
|
212
|
+
Parameters
|
|
213
|
+
----------
|
|
214
|
+
rgb : tuple[np.ndarray, np.ndarray, np.ndarray]
|
|
215
|
+
RGB image channels as (red, green, blue).
|
|
216
|
+
|
|
217
|
+
Returns
|
|
218
|
+
-------
|
|
219
|
+
np.ndarray
|
|
220
|
+
VARI image with dtype float32.
|
|
221
|
+
"""
|
|
222
|
+
red, green, blue = (band.astype(np.float32) for band in rgb)
|
|
223
|
+
|
|
224
|
+
numerator = green - red
|
|
225
|
+
denominator = green + red - blue
|
|
226
|
+
|
|
227
|
+
result = np.full(numerator.shape, np.nan, dtype=np.float32)
|
|
228
|
+
|
|
229
|
+
np.divide(
|
|
230
|
+
numerator,
|
|
231
|
+
denominator,
|
|
232
|
+
out=result,
|
|
233
|
+
where=denominator != 0,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
return result
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def compute_gli(
|
|
240
|
+
rgb: tuple[np.ndarray, np.ndarray, np.ndarray],
|
|
241
|
+
) -> np.ndarray:
|
|
242
|
+
"""Compute the Green Leaf Index (GLI).
|
|
243
|
+
|
|
244
|
+
Parameters
|
|
245
|
+
----------
|
|
246
|
+
rgb : tuple[np.ndarray, np.ndarray, np.ndarray]
|
|
247
|
+
RGB image channels as (red, green, blue).
|
|
248
|
+
|
|
249
|
+
Returns
|
|
250
|
+
-------
|
|
251
|
+
np.ndarray
|
|
252
|
+
GLI image with dtype float32.
|
|
253
|
+
"""
|
|
254
|
+
red, green, blue = (band.astype(np.float32) for band in rgb)
|
|
255
|
+
|
|
256
|
+
numerator = 2 * green - red - blue
|
|
257
|
+
denominator = 2 * green + red + blue
|
|
258
|
+
|
|
259
|
+
result = np.full(numerator.shape, np.nan, dtype=np.float32)
|
|
260
|
+
|
|
261
|
+
np.divide(
|
|
262
|
+
numerator,
|
|
263
|
+
denominator,
|
|
264
|
+
out=result,
|
|
265
|
+
where=denominator != 0,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
return result
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def compute_rgbvi(
|
|
272
|
+
rgb: tuple[np.ndarray, np.ndarray, np.ndarray],
|
|
273
|
+
) -> np.ndarray:
|
|
274
|
+
"""Compute the Red Green Blue Vegetation Index (RGBVI).
|
|
275
|
+
|
|
276
|
+
Parameters
|
|
277
|
+
----------
|
|
278
|
+
rgb : tuple[np.ndarray, np.ndarray, np.ndarray]
|
|
279
|
+
RGB image channels as (red, green, blue).
|
|
280
|
+
|
|
281
|
+
Returns
|
|
282
|
+
-------
|
|
283
|
+
np.ndarray
|
|
284
|
+
RGBVI image with dtype float32.
|
|
285
|
+
"""
|
|
286
|
+
red, green, blue = (band.astype(np.float32) for band in rgb)
|
|
287
|
+
|
|
288
|
+
green_squared = green**2
|
|
289
|
+
red_blue = red * blue
|
|
290
|
+
|
|
291
|
+
numerator = green_squared - red_blue
|
|
292
|
+
denominator = green_squared + red_blue
|
|
293
|
+
|
|
294
|
+
result = np.full(numerator.shape, np.nan, dtype=np.float32)
|
|
295
|
+
|
|
296
|
+
np.divide(
|
|
297
|
+
numerator,
|
|
298
|
+
denominator,
|
|
299
|
+
out=result,
|
|
300
|
+
where=denominator != 0,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
return result
|
|
304
|
+
|
|
147
305
|
VEGETATION_INDICES = {
|
|
306
|
+
# Satellite
|
|
148
307
|
"ndvi": {
|
|
149
308
|
"function": compute_ndvi,
|
|
150
309
|
"bands": ["red_band", "nir_band"],
|
|
@@ -161,6 +320,37 @@ VEGETATION_INDICES = {
|
|
|
161
320
|
"function": compute_gndvi,
|
|
162
321
|
"bands": ["nir_band", "green_band"],
|
|
163
322
|
},
|
|
323
|
+
# UAV RGB
|
|
324
|
+
"exg": {
|
|
325
|
+
"bands": ["red_band", "green_band", "blue_band"],
|
|
326
|
+
"function": compute_exg,
|
|
327
|
+
"input_type": "rgb",
|
|
328
|
+
},
|
|
329
|
+
"exr": {
|
|
330
|
+
"bands": ["red_band", "green_band", "blue_band"],
|
|
331
|
+
"function": compute_exr,
|
|
332
|
+
"input_type": "rgb",
|
|
333
|
+
},
|
|
334
|
+
"exgr": {
|
|
335
|
+
"bands": ["red_band", "green_band", "blue_band"],
|
|
336
|
+
"function": compute_exgr,
|
|
337
|
+
"input_type": "rgb",
|
|
338
|
+
},
|
|
339
|
+
"vari": {
|
|
340
|
+
"bands": ["red_band", "green_band", "blue_band"],
|
|
341
|
+
"function": compute_vari,
|
|
342
|
+
"input_type": "rgb",
|
|
343
|
+
},
|
|
344
|
+
"gli": {
|
|
345
|
+
"bands": ["red_band", "green_band", "blue_band"],
|
|
346
|
+
"function": compute_gli,
|
|
347
|
+
"input_type": "rgb",
|
|
348
|
+
},
|
|
349
|
+
"rgbvi": {
|
|
350
|
+
"bands": ["red_band", "green_band", "blue_band"],
|
|
351
|
+
"function": compute_rgbvi,
|
|
352
|
+
"input_type": "rgb",
|
|
353
|
+
},
|
|
164
354
|
}
|
|
165
355
|
|
|
166
356
|
def compute_indexes(
|
|
@@ -168,6 +358,7 @@ def compute_indexes(
|
|
|
168
358
|
nir_band: np.ndarray | None = None,
|
|
169
359
|
green_band: np.ndarray | None = None,
|
|
170
360
|
red_edge_band: np.ndarray | None = None,
|
|
361
|
+
blue_band: np.ndarray | None = None,
|
|
171
362
|
indices: list[str] | None = None,
|
|
172
363
|
) -> dict[str, np.ndarray]:
|
|
173
364
|
"""Compute vegetation indices from available spectral bands.
|
|
@@ -182,6 +373,8 @@ def compute_indexes(
|
|
|
182
373
|
Green spectral band.
|
|
183
374
|
red_edge_band : np.ndarray or None, optional
|
|
184
375
|
Red-edge spectral band.
|
|
376
|
+
blue_band : np.ndarray or None, optional
|
|
377
|
+
Blue spectral band.
|
|
185
378
|
indices : list[str] or None, optional
|
|
186
379
|
Names of the vegetation indices to compute. If None, all
|
|
187
380
|
available vegetation indices are requested.
|
|
@@ -201,13 +394,14 @@ def compute_indexes(
|
|
|
201
394
|
results = {}
|
|
202
395
|
|
|
203
396
|
if indices is None:
|
|
204
|
-
indices =
|
|
397
|
+
indices = list(VEGETATION_INDICES)
|
|
205
398
|
|
|
206
399
|
available_bands = {
|
|
207
400
|
"red_band": red_band,
|
|
208
401
|
"nir_band": nir_band,
|
|
209
402
|
"green_band": green_band,
|
|
210
403
|
"red_edge_band": red_edge_band,
|
|
404
|
+
"blue_band": blue_band,
|
|
211
405
|
}
|
|
212
406
|
|
|
213
407
|
for index in indices:
|
|
@@ -227,27 +421,9 @@ def compute_indexes(
|
|
|
227
421
|
for band in required_bands
|
|
228
422
|
]
|
|
229
423
|
|
|
230
|
-
|
|
424
|
+
if index_config.get("input_type") == "rgb":
|
|
425
|
+
results[index] = compute_function(tuple(band_arrays))
|
|
426
|
+
else:
|
|
427
|
+
results[index] = compute_function(*band_arrays)
|
|
231
428
|
|
|
232
|
-
return results
|
|
233
|
-
|
|
234
|
-
def compute_exg(
|
|
235
|
-
rgb: tuple[np.ndarray, np.ndarray, np.ndarray],
|
|
236
|
-
) -> np.ndarray:
|
|
237
|
-
"""Compute the Excess Green Index (ExG).
|
|
238
|
-
|
|
239
|
-
Parameters
|
|
240
|
-
----------
|
|
241
|
-
rgb : tuple[np.ndarray, np.ndarray, np.ndarray]
|
|
242
|
-
RGB image channels as (red, green, blue).
|
|
243
|
-
|
|
244
|
-
Returns
|
|
245
|
-
-------
|
|
246
|
-
np.ndarray
|
|
247
|
-
ExG image with dtype float32.
|
|
248
|
-
"""
|
|
249
|
-
red = rgb[0].astype(np.float32)
|
|
250
|
-
green = rgb[1].astype(np.float32)
|
|
251
|
-
blue = rgb[2].astype(np.float32)
|
|
252
|
-
|
|
253
|
-
return 2 * green - red - blue
|
|
429
|
+
return results
|