diff --git a/.github/workflows/build_and_test_app.yml b/.github/workflows/build_and_test_app.yml index cac755c..c17082d 100644 --- a/.github/workflows/build_and_test_app.yml +++ b/.github/workflows/build_and_test_app.yml @@ -3,7 +3,7 @@ name: ALCD on: pull_request: branches: - - "new_add_doc" + - "master" permissions: contents: read @@ -58,4 +58,4 @@ jobs: export OTB_INSTALL_DIR=/opt/otb export LC_NUMERIC=C - pytest -s --cov-fail-under=65 \ No newline at end of file + pytest -s --cov-fail-under=65 diff --git a/docs/source/images/colorized_classif_1.png b/docs/source/images/colorized_classif_1.png new file mode 100644 index 0000000..061bbe5 Binary files /dev/null and b/docs/source/images/colorized_classif_1.png differ diff --git a/docs/source/images/colorized_classif_2.png b/docs/source/images/colorized_classif_2.png new file mode 100644 index 0000000..8f9ff30 Binary files /dev/null and b/docs/source/images/colorized_classif_2.png differ diff --git a/docs/source/images/quicklook_1.png b/docs/source/images/quicklook_1.png new file mode 100644 index 0000000..eaf67fb Binary files /dev/null and b/docs/source/images/quicklook_1.png differ diff --git a/docs/source/images/quicklook_2.png b/docs/source/images/quicklook_2.png new file mode 100644 index 0000000..eaf67fb Binary files /dev/null and b/docs/source/images/quicklook_2.png differ diff --git a/docs/source/index.md b/docs/source/index.md index e7a9a48..428aee9 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -14,7 +14,7 @@ For a quick start, you can directly go to the {doc}`tutorial ` parts. How to install ALCD configure ALCD -ACLD workflow +ALCD workflow Tutorials Tips and advice Complementary information \ No newline at end of file diff --git a/docs/source/notebooks/montreux.ipynb b/docs/source/notebooks/montreux.ipynb index 3d051f9..1caab3e 100644 --- a/docs/source/notebooks/montreux.ipynb +++ b/docs/source/notebooks/montreux.ipynb @@ -17,8 +17,8 @@ "\n", "ALCD requires several types of data for its execution\n", "\n", - " - raster data on which the classification iterations will be performed.\n", - " - json files to parameterise the chain.\n", + " - raster data on which the classification iterations will be performed. This data can be directly downloaded on [Geodes](https://geodes-portal.cnes.fr/) by selecting the date, collection, type of product and cloud cover desired.\n", + " - json files to parameterise the chain. These files can be filled by the user (see [Contents of the configuration files section](montreux.ipynb#contents-of-the-configuration-files))\n", "\n", "The following sections will focus on the presentation of this data.\n", "\n", @@ -32,14 +32,14 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "id": "99bb6b3e-3baf-4652-8924-c1c8ac47556c", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "853e763d423e46e3a03b6dfc4ca39b8e", + "model_id": "b7f9d564b3b042779c1c861cf9911626", "version_major": 2, "version_minor": 0 }, @@ -53,7 +53,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "ed53896a773f465da1f70801290550ac", + "model_id": "adf612381751475b8a94533cb4f8eaac", "version_major": 2, "version_minor": 0 }, @@ -264,21 +264,12 @@ "execution_count": 3, "id": "ec8d0240-f49b-43ba-9b48-507b41a66ec7", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/ecadaux/miniconda3/envs/env_otb2/lib/python3.11/site-packages/osgeo/gdal.py:287: FutureWarning: Neither gdal.UseExceptions() nor gdal.DontUseExceptions() has been explicitly called. In GDAL 4.0, exceptions will be enabled by default.\n", - " warnings.warn(\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "# considering PATH_ALCD is the path to the script 'all_run_alcd.py'\n", "export PATH_ALCD=\"../../..\"\n", - "python $PATH_ALCD/all_run_alcd.py -force True -s 0 -f True -l Montreux -d 20240213 -c 20240213 -dates False -kfold False -global_parameters global_parameters.json -paths_parameters paths_configuration.json -model_parameters model_parameters.json > tuto_log.txt" + "python $PATH_ALCD/all_run_alcd.py -force True -s 0 -f True -l Montreux -d 20240213 -c 20240213 -dates False -kfold False -global_parameters global_parameters.json -paths_parameters paths_configuration.json -model_parameters model_parameters.json > tuto_log.txt 2>/dev/null" ] }, { @@ -300,7 +291,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "54378f883c6e421d9a5a201a6dd60063", + "model_id": "6b9d43ae4fcb46a19055034f75988224", "version_major": 2, "version_minor": 0 }, @@ -314,7 +305,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "0ca57bce63b946c3af59d67828bbeeec", + "model_id": "d51dee540def4bfa9ab6d6ac9124011e", "version_major": 2, "version_minor": 0 }, @@ -383,60 +374,21 @@ "\n", "### Launching command \n", "\n", - "The command to run is almost identical to the previous one, except that the ``s`` option changes from 0 to 1." + "The command to run is almost identical to the previous one, except that the ``s`` option changes from 0 to 1. This cell may take several seconds to run." ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 3, "id": "3d43025d-bde6-426c-bd59-7a49dc485e2e", "metadata": { "scrolled": true }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/ecadaux/miniconda3/envs/env_otb2/lib/python3.11/site-packages/osgeo/ogr.py:560: FutureWarning: Neither ogr.UseExceptions() nor ogr.DontUseExceptions() has been explicitly called. In GDAL 4.0, exceptions will be enabled by default.\n", - " warnings.warn(\n", - "WARNING: In /work/THEIA/CES/oso/CD/conda_env/iota2_build_env/conda-bld/otb_1736246894519/work/OTB/Modules/Filtering/Statistics/src/otbSamplerBase.cxx, line 42\n", - "RandomSampler (0x5908d47b4570): Needed elements (8000) will be clamped to total elements (165)\n", - "\n", - "\n", - "WARNING: In /work/THEIA/CES/oso/CD/conda_env/iota2_build_env/conda-bld/otb_1736246894519/work/OTB/Modules/Filtering/Statistics/src/otbSamplerBase.cxx, line 42\n", - "RandomSampler (0x5908d4bb7fc0): Needed elements (8000) will be clamped to total elements (164)\n", - "\n", - "\n", - "WARNING: In /work/THEIA/CES/oso/CD/conda_env/iota2_build_env/conda-bld/otb_1736246894519/work/OTB/Modules/Filtering/Statistics/src/otbSamplerBase.cxx, line 42\n", - "RandomSampler (0x5908d4c61110): Needed elements (8000) will be clamped to total elements (177)\n", - "\n", - "\n", - "WARNING: In /work/THEIA/CES/oso/CD/conda_env/iota2_build_env/conda-bld/otb_1736246894519/work/OTB/Modules/Filtering/Statistics/src/otbSamplerBase.cxx, line 42\n", - "RandomSampler (0x5908d47b2eb0): Needed elements (8000) will be clamped to total elements (147)\n", - "\n", - "\n", - "WARNING: In /work/THEIA/CES/oso/CD/conda_env/iota2_build_env/conda-bld/otb_1736246894519/work/OTB/Modules/Filtering/Statistics/src/otbSamplerBase.cxx, line 42\n", - "RandomSampler (0x5908d47b2070): Needed elements (8000) will be clamped to total elements (172)\n", - "\n", - "\n", - "/home/ecadaux/miniconda3/envs/env_otb2/lib/python3.11/site-packages/numpy/core/fromnumeric.py:3504: RuntimeWarning: Mean of empty slice.\n", - " return _methods._mean(a, axis=axis, dtype=dtype,\n", - "/home/ecadaux/miniconda3/envs/env_otb2/lib/python3.11/site-packages/numpy/core/_methods.py:129: RuntimeWarning: invalid value encountered in divide\n", - " ret = ret.dtype.type(ret / rcount)\n", - "/home/ecadaux/miniconda3/envs/env_otb2/lib/python3.11/site-packages/numpy/core/_methods.py:206: RuntimeWarning: Degrees of freedom <= 0 for slice\n", - " ret = _var(a, axis=axis, dtype=dtype, out=out, ddof=ddof,\n", - "/home/ecadaux/miniconda3/envs/env_otb2/lib/python3.11/site-packages/numpy/core/_methods.py:163: RuntimeWarning: invalid value encountered in divide\n", - " arrmean = um.true_divide(arrmean, div, out=arrmean,\n", - "/home/ecadaux/miniconda3/envs/env_otb2/lib/python3.11/site-packages/numpy/core/_methods.py:198: RuntimeWarning: invalid value encountered in divide\n", - " ret = ret.dtype.type(ret / rcount)\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "export PATH_ALCD=\"../../..\"\n", - "python $PATH_ALCD/all_run_alcd.py -force True -s 1 -f True -l Montreux -d 20240213 -c 20240213 -dates False -kfold False -global_parameters global_parameters.json -paths_parameters paths_configuration.json -model_parameters model_parameters.json > tuto_log.txt" + "python $PATH_ALCD/all_run_alcd.py -force True -s 1 -f True -l Montreux -d 20240213 -c 20240213 -dates False -kfold False -global_parameters global_parameters.json -paths_parameters paths_configuration.json -model_parameters model_parameters.json > tuto_log.txt 2>/dev/null" ] }, { @@ -451,14 +403,14 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 6, "id": "bdbb6818-5d4c-4461-8e73-09166ad8580f", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "0f34354a34b24fc59d1972d87aa9e8a2", + "model_id": "17f9e12f76bb41528fb02822b874890c", "version_major": 2, "version_minor": 0 }, @@ -472,7 +424,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "487bfc0b6f8c49569262ff52c71d139d", + "model_id": "51455cfbf17f43ad9559e90d0188d563", "version_major": 2, "version_minor": 0 }, @@ -510,19 +462,28 @@ "\n", "Here are the results after the first classification by ALCD.\n", "\n", - "
\n", - "
\n", - " \"Image\n", - "
Sentinel-2 acquisition
\n", - "
\n", - "
\n", - " \"Image\n", - "
Sentinel-2 classified
\n", - "
\n", - "
\n", - " \"Image\n", - "
legend
\n", - "
\n", + "
\n", + "\n", + " \"Image\n", + " \n", + "

Sentinel-2 acquisition

\n", + "\n", + "
\n", + "\n", + "
\n", + " \n", + " \"Image\n", + " \n", + "

Sentinel-2 classified

\n", + "\n", + "
\n", + "\n", + "
\n", + " \n", + " \"Image\n", + " \n", + "

Classification legend

\n", + "\n", "
" ] }, @@ -538,7 +499,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 7, "id": "bb41460f-0c58-4629-80ba-70c2b9b7f940", "metadata": {}, "outputs": [], @@ -558,49 +519,20 @@ "\n", "After this command has been run, it is possible to modify the initial training database. In our case, the results for water were disappointing. We will add water samples.\n", "\n", - "Then run ALCD again with the command" + "Then run ALCD again with the following command. This cell may take several seconds to run." ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "id": "50c5869e-6851-43ea-aae4-6ff7c0b5f624", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/ecadaux/miniconda3/envs/env_otb2/lib/python3.11/site-packages/osgeo/ogr.py:560: FutureWarning: Neither ogr.UseExceptions() nor ogr.DontUseExceptions() has been explicitly called. In GDAL 4.0, exceptions will be enabled by default.\n", - " warnings.warn(\n", - "WARNING: In /work/THEIA/CES/oso/CD/conda_env/iota2_build_env/conda-bld/otb_1736246894519/work/OTB/Modules/Filtering/Statistics/src/otbSamplerBase.cxx, line 42\n", - "RandomSampler (0x6007486be5f0): Needed elements (8000) will be clamped to total elements (161)\n", - "\n", - "\n", - "WARNING: In /work/THEIA/CES/oso/CD/conda_env/iota2_build_env/conda-bld/otb_1736246894519/work/OTB/Modules/Filtering/Statistics/src/otbSamplerBase.cxx, line 42\n", - "RandomSampler (0x600748c28930): Needed elements (8000) will be clamped to total elements (161)\n", - "\n", - "\n", - "WARNING: In /work/THEIA/CES/oso/CD/conda_env/iota2_build_env/conda-bld/otb_1736246894519/work/OTB/Modules/Filtering/Statistics/src/otbSamplerBase.cxx, line 42\n", - "RandomSampler (0x6007485d6b50): Needed elements (8000) will be clamped to total elements (174)\n", - "\n", - "\n", - "WARNING: In /work/THEIA/CES/oso/CD/conda_env/iota2_build_env/conda-bld/otb_1736246894519/work/OTB/Modules/Filtering/Statistics/src/otbSamplerBase.cxx, line 42\n", - "RandomSampler (0x6007485d80a0): Needed elements (8000) will be clamped to total elements (150)\n", - "\n", - "\n", - "WARNING: In /work/THEIA/CES/oso/CD/conda_env/iota2_build_env/conda-bld/otb_1736246894519/work/OTB/Modules/Filtering/Statistics/src/otbSamplerBase.cxx, line 42\n", - "RandomSampler (0x6007485d6800): Needed elements (8000) will be clamped to total elements (175)\n", - "\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "%%bash\n", "# considering PATH_ALCD is the path to the script 'all_run_alcd.py'\n", "export PATH_ALCD=\"../../..\"\n", - "python $PATH_ALCD/all_run_alcd.py -force False -s 1 -f False -l Montreux -d 20240213 -c 20240213 -dates False -kfold False -global_parameters global_parameters.json -paths_parameters paths_configuration.json -model_parameters model_parameters.json > log_tuto.txt" + "python $PATH_ALCD/all_run_alcd.py -force False -s 1 -f False -l Montreux -d 20240213 -c 20240213 -dates False -kfold False -global_parameters global_parameters.json -paths_parameters paths_configuration.json -model_parameters model_parameters.json > log_tuto.txt 2>/dev/null" ] }, { @@ -612,19 +544,28 @@ "source": [ "The results generated during this iteration overwrite the previous ones. With the addition of samples, we can see an evolution in the classification\n", "\n", - "
\n", - "
\n", - " \"Image\n", - "
Sentinel-2 acquisition
\n", - "
\n", - "
\n", - " \"Image\n", - "
Sentinel-2 classified
\n", - "
\n", - "
\n", - " \"Image\n", - "
legend
\n", - "
\n", + "
\n", + "\n", + " \"Image\n", + " \n", + "

Sentinel-2 acquisition

\n", + "\n", + "
\n", + "\n", + "
\n", + " \n", + " \"Image\n", + " \n", + "

Sentinel-2 classified

\n", + "\n", + "
\n", + "\n", + "
\n", + " \n", + " \"Image\n", + " \n", + "

Classification legend

\n", + "\n", "
\n", "\n", "## Advance usages\n", @@ -720,7 +661,7 @@ "\t},\n", "```\n", "\n", - "### User features\n", + "### User features \n", "\n", "Users can integrate custom processes to modify the input data before classification by adding *\"user_function\"* and *\"user_module\"* fields in the \"user_choices\" section of the *global_parameters.json* file:\n", "\n", @@ -787,7 +728,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.11.11" } }, "nbformat": 4, diff --git a/docs/source/tutorial.md b/docs/source/tutorial.md index 0851237..1b21983 100644 --- a/docs/source/tutorial.md +++ b/docs/source/tutorial.md @@ -1,7 +1,6 @@ # ALCD tutorial -This is a step-by-step tutorial, to help you use the ALCD algorithm. Here, we will classify the -clouds on the image of Arles, on the 2 nd of October, 2017. +This is a step-by-step tutorial, to help you use the ALCD algorithm. The expected result is the following: @@ -34,41 +33,11 @@ set it to 0, modify the masks, and then set it to 1. available samples. - ``dates``: boolean. If set to True, ALCD will display the available dates for the given location. -- ``global_parameters``: path to json file which parametrize ALCD -- ``paths_parameters``: path to json file which contain useful paths for ALCD -- ``model_parameters``: path to json file which contain classifier parameters +- ``global_parameters``: path to json file which parametrize ALCD (for more information, please see the `configuration documentation `.) +- ``paths_parameters``: path to json file which contain useful paths for ALCD (for more information, please see the `configuration documentation `.) +- ``model_parameters``: path to json file which contain classifier parameters (for more information, please see the `configuration documentation `.) -## Summary of the commands -The detailed steps are given after this part. We give here the summary of the commands to -use, so you can come back here if you forget how to use ALCD. - -See the available dates. - -```bash -python all_run_alcd.py −l Arles −dates True -``` - -Initialisation and creation of the features. -```bash -python all_run_alcd.py −f True −s 0 − l Arles −d 20171002 −c 20171005 -``` - -Edit the shapefiles to populate them with manually labeled samples. Then run the algorithm. -```bash -python all_run_alcd.py −f True −s 1 -``` - -While the results are not satisfactory: -Visualize the results. Save the current iteration. -```bash -python all_run_alcd.py −f False −s 0 -``` - -Edit the shapefiles to your convenience. Run the algorithm again. -```bash -python all_run_alcd.py −f False −s 1 -``` ## Paths preparation @@ -76,34 +45,19 @@ Before running anything, you need to set the correct paths and parameters. In the ``paths_configuration.json``: - Add the tile code linked to the location you want to add - Create the output directory for ALCD, and set its path in the "data_alcd" variable -- Set the correct paths for the L1C directory and the DTM_input +- Set the correct paths for the L1C directory and the DTM_input. In the global_parameters.json, if you use a distant and a local machine, set the ``local_paths`` variables accordingly. ## Step 1 -First of all, you must pick the date you are interested in. As the code will run on the L1C -product, you can list all the available dates with the command - -```bash -python all_run_alcd.py −l Arles −dates True -``` - -You should get a list like - -```python -["20151202" , "20151230" ,... , "20180319" , "20180321"] -``` - -A good practice is to visualise the two dates we want to use beforehand. This can be facil- -itated by the code quicklook_generator.py, which generates quicklooks for a given location. +A good practice is to visualise the two dates we want to use beforehand. This can be +facilitated by the code *quicklook_generator.py*, which generates quicklooks for a given location. The user can therefore make sure that the cloud-free image is indeed cloud-free, and that the image to be classified is interesting. -As stated above, the date we will use here is 20171002. This date was acquired just before -a cloud free date: 20171005. -Therefore, initialize the environment by running +Therefore, initialize the environment by running : ```bash -python all_run_alcd.py −f True −s 0 − l Arles −d 20171002 −c 20171005 +python all_run_alcd.py -f True -s 0 -l city_name_dir -d cloudy_date -c clear_date -kfold False -global_parameters path_of_global_parameters.json -paths_parameters path_of_paths_configuration.json -model_parameters path_of_model_parameters.json ``` This will create the concatenated .tif with all the bands, and empty shapefiles for each class, @@ -115,14 +69,13 @@ Step 2. ## Step 2 -You can now open QGIS. Open the raster ``In_data/Image/Arles_bands_H.tif`` (H stands for -Heavy, as it is in full resolution of 20m per pixel), and ``In_data/Image/Arles_bands.tif``. -The ``Arles_bands_H.tif`` bands refer to the band 2 (blue), 3 (green), 4 (red), 10 (the band at -1375nm), the NDVI and the NDWI. The bands for the ``Arles_bands.tif`` are quite numerous, +You can now open QGIS. Open the raster ``In_data/Image/city_name_bands_H.tif`` (H stands for +Heavy, as it is in full resolution of 20m per pixel), and ``In_data/Image/city_name_bands.tif``. +The ``city_name_bands_H.tif`` bands refer to the band 2 (blue), 3 (green), 4 (red), 10 (the band at +1375nm), the NDVI and the NDWI. The bands for the ``city_name_bands.tif`` are quite numerous, but the content of each band is documented in the .txt file corresponding to each .tif. Now, adjust the style in QGIS such that you see the image in true colors. For that, you -can load the file ``color_tables/heavy_tif_true_colors_style.qml`` on the Heavy .tif. You -should get : +can load the file ``color_tables/heavy_tif_true_colors_style.qml`` on the Heavy .tif. You should get :
@@ -132,7 +85,7 @@ should get :
Now, load all the empty shapefiles from the directory ``In_data/Masks``. -If you display a band being a time difference (for example the 20th band of ``Arles_bands. +If you display a band being a time difference (for example the 20th band of ``city_name_bands. tif``), you will observe that there was no data on the bottom-right corner for the clear date. The same is true with the top-left corner for the cloudy date. Thus, the no_data file already has some data (which is the case if one or both of the original @@ -152,8 +105,8 @@ user add samples in these areas by mistake. ## Step 3 -It is now time to edit the masks layers. For each class (land, low clouds, etc), edit the corre- -sponding layer. Add the points that you want to take as samples, by clicking on the image and +It is now time to edit the masks layers. For each class (land, low clouds, etc), edit the corresponding +layer. Add the points that you want to take as samples, by clicking on the image and pressing Enter for each point. We have found more efficient to use points rather than polygons, we later dilate the points by 3 pixels assuming the neighbourhood is homogeneous in terms of class, so you should avoid to use a pixel just at the edge of a feature (cloud, land). @@ -192,10 +145,10 @@ At the end, you obtain ``Figure 5`` ## Step 4 Now, copy back the edited masks to the distant machine, or skip this if you work on one machine. -It is time to train the model, and classify the image! Do it with +It is time to train the model, and classify the image! Do it with : ```bash -python all_run_alcd.py −f True −s 1 +python all_run_alcd.py -f True -s 1 -l city_name_dir -d cloudy_date -c clear_date -kfold False -global_parameters path_of_global_parameters.json -paths_parameters path_of_paths_configuration.json -model_parameters path_of_model_parameters.json ``` The results can be seen in the Out directory. The regularized classification map is labeled_ @@ -220,10 +173,10 @@ advantage of this program: the active learning. ## Step 5 -Do an new iteration, by running +Do an new iteration, by running : ```bash -python all_run_alcd.py −f False −s 0 +python all_run_alcd.py -f False -s 0 -l city_name_dir -d cloudy_date -c clear_date -kfold False -global_parameters path_of_global_parameters.json -paths_parameters path_of_paths_configuration.json -model_parameters path_of_model_parameters.json ``` It will save the previous iteration, and you can now edit the class layers, by adding new @@ -266,10 +219,10 @@ our output, as shown in ``Figure 9``. Do this for the areas where a misclassification is visible. Once the wanted points in each class have been added, you can copy back the layers to the distant machine with the appropriate command. -Finally, you run once again the training and the classification with +Finally, you run once again the training and the classification with : ```bash -python all_run_alcd.py −f False −s 1 +python all_run_alcd.py -f False -s 1 -l city_name_dir -d cloudy_date -c clear_date -kfold False -global_parameters path_of_global_parameters.json -paths_parameters path_of_paths_configuration.json -model_parameters path_of_model_parameters.json ``` ## Step 6 @@ -278,10 +231,10 @@ Repeat the Step 5 until you are satisfied with the classification the ALCD algor Quick tip: some data (30% by default) are used for the validation of the model, i.e. just to compute statistics. If you want to have more samples that you add manually to be taken -into account for the training part, you can increase the training_proportion in the ``global_ -parameters.json``. +into account for the training part, you can increase the training_proportion in the +``global_parameters.json``. -Here is an example of the classification that you could obtain after each iteration. The 6 th +Here is an example of the classification that you could obtain after each iteration. The 6th one is considered to be good (by myself), so you can stop there.
@@ -316,4 +269,4 @@ classification, and with the confidence map, are given in ``Figures 11, 12 and 1
-{doc}`water_mask `. +