diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b7d1566c1c7301e0672a1c5bc1792e91cec61835..7301254a9fd72f649459f1689f6804486d4fc44a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,8 +4,8 @@ variables: GPU_IMAGE_NAME: $CI_REGISTRY_IMAGE:gpu DOCKER_BUILDKIT: 1 DOCKER_DRIVER: overlay2 - CPU_BASE_IMAGE: gitlab-registry.irstea.fr/remi.cresson/otbtf/3.2.1:cpu-basic-dev - GPU_BASE_IMAGE: gitlab-registry.irstea.fr/remi.cresson/otbtf/3.2.1:gpu-basic-dev + CPU_BASE_IMAGE: gitlab-registry.irstea.fr/remi.cresson/otbtf:3.3.2-cpu-dev + GPU_BASE_IMAGE: gitlab-registry.irstea.fr/remi.cresson/otbtf:3.3.2-gpu-dev workflow: rules: diff --git a/Dockerfile b/Dockerfile index 684ed0872f164f19df739369495228bb593b944a..c2debd3b41275b3c5a29c812dd754ff1012b638f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,9 @@ # Decloud dockerfile # To build the docker image for cpu, do the following: # -# docker build --build-arg "BASE_IMAGE=mdl4eo/otbtf3.0:cpu-basic-dev" . +# docker build --build-arg "BASE_IMAGE=mdl4eo/otbtf3.3.2:cpu-dev" . # -ARG BASE_IMAGE=mdl4eo/otbtf3.0:gpu-dev +ARG BASE_IMAGE=mdl4eo/otbtf3.3.2:gpu-dev FROM $BASE_IMAGE LABEL description="Decloud docker image" LABEL maintainer="Remi Cresson [at] inrae [dot] fr" @@ -18,9 +18,6 @@ RUN apt upgrade -y COPY docker/requirements.txt /tmp/requirements.txt RUN python3 -m pip install -r /tmp/requirements.txt -# Fix Mosaic app in OTB <= 7.4 -RUN cd /src/otb/otb/ && git config remote.origin.fetch refs/heads/*:refs/remotes/origin/* && git fetch origin && git -c user.name=decloud-docker -c user.email=decloud@decloud.xyz cherry-pick 44ec133646c46d6772450bbf5fb3ec54e282f56b - # Build remote modules RUN cd /src/otb/otb/Modules/Remote/ && git clone https://gitlab.irstea.fr/remi.cresson/SimpleExtractionTools.git RUN cd /src/otb/otb/Modules/Remote/ && git clone https://gitlab.irstea.fr/remi.cresson/mlutils.git diff --git a/decloud/analysis/images_stats.py b/decloud/analysis/images_stats.py index a38da0e027ab2218dcea5664e9e86ea774d4c929..dac37eda0ba52ef9103c4660d1a66d23013b2bfe 100644 --- a/decloud/analysis/images_stats.py +++ b/decloud/analysis/images_stats.py @@ -31,7 +31,7 @@ import sys import json import numpy as np from matplotlib import pyplot as plt -import gdal +from osgeo import gdal parser = argparse.ArgumentParser(description="dataset test") parser.add_argument("--input_dir", help="A directory containing S1 or S2 files", required=True) diff --git a/decloud/core/system.py b/decloud/core/system.py index 0b9f47558187daaea4af12b43c058f87da969973..d43fb85bf77291d7dc3d31adfdf0b4aca714b791 100644 --- a/decloud/core/system.py +++ b/decloud/core/system.py @@ -41,16 +41,13 @@ def get_commit_hash(): """ Return the git hash of the repository """ repo = git.Repo(os.path.dirname(os.path.realpath(__file__)), search_parent_directories=True) - commit_hash = "nohash" try: - commit_hash = repo.head.object.hexsha[0:5] - except (ValueError, TypeError) as e: - print(f"Unable to get commit hash! {e}") - - try: - commit_hash = repo.active_branch.name + "_" + commit_hash - except (ValueError, TypeError) as e: - print(f"Unable to get branch name! {e}") + commit_hash = repo.active_branch.name + "_" + repo.head.object.hexsha[0:5] + except (TypeError, ValueError, BrokenPipeError): + try: + commit_hash = 'DETACHED_' + repo.head.object.hexsha[0:5] + except (ValueError, BrokenPipeError): + commit_hash = 'DETACHED' return commit_hash diff --git a/decloud/preprocessing/dem_prepare.py b/decloud/preprocessing/dem_prepare.py index 35c3b90e8a18b92018e6eeebc12eba6b2185ab4c..f55338ab1d955213675a75b9c451c57c733f5734 100644 --- a/decloud/preprocessing/dem_prepare.py +++ b/decloud/preprocessing/dem_prepare.py @@ -25,7 +25,7 @@ DEALINGS IN THE SOFTWARE. import argparse import logging import elevation -import gdal +from osgeo import gdal import decloud.preprocessing.constants as constants import otbApplication as otb from decloud.core import system diff --git a/decloud/production/inference.py b/decloud/production/inference.py index e85544ac142bac07d20a1b93ed6360a4e7e43389..4aa8df30d000c8411d82c585119aa5b5e3f2401d 100644 --- a/decloud/production/inference.py +++ b/decloud/production/inference.py @@ -66,7 +66,7 @@ def inference(sources, sources_scales, pad, ts, savedmodel_dir, out_tensor, out_ # Setup TensorFlowModelServe system.set_env_var("OTB_TF_NSOURCES", str(len(sources))) - infer = pyotb.TensorflowModelServe(execute=False) + parameters = {} # Inputs for i, (placeholder, source) in enumerate(sources.items()): @@ -80,18 +80,18 @@ def inference(sources, sources_scales, pad, ts, savedmodel_dir, out_tensor, out_ if placeholder in sources_scales: src_rfield = int(rfield / sources_scales[placeholder]) - infer.set_parameters({get_key("il"): [source], - get_key("rfieldx"): src_rfield, - get_key("rfieldy"): src_rfield, - get_key("placeholder"): placeholder}) + parameters.update({get_key("il"): [source], + get_key("rfieldx"): src_rfield, + get_key("rfieldy"): src_rfield, + get_key("placeholder"): placeholder}) # Model - infer.set_parameters({"model.dir": savedmodel_dir, "model.fullyconv": "on", - "output.names": [padded_tensor_name(out_tensor, pad)], - "output.efieldx": efield, "output.efieldy": efield, - "optim.tilesizex": efield, "optim.tilesizey": efield, - "optim.disabletiling": 1}) - infer.Execute() + parameters.update({"model.dir": savedmodel_dir, "model.fullyconv": True, + "output.names": [padded_tensor_name(out_tensor, pad)], + "output.efieldx": efield, "output.efieldy": efield, + "optim.tilesizex": efield, "optim.tilesizey": efield, + "optim.disabletiling": True}) + infer = pyotb.TensorflowModelServe(parameters) # Post Processing # For ESA Sentinel-2, remove potential zeros the network may have introduced in the valid parts of the image diff --git a/decloud/production/monthly_synthesis_processor_s2.py b/decloud/production/monthly_synthesis_processor_s2.py index e2ad8c4cb79e748dd86500517e3914761f695b31..8180c64fe278eee9adaf5db61dcff1d86c27386c 100644 --- a/decloud/production/monthly_synthesis_processor_s2.py +++ b/decloud/production/monthly_synthesis_processor_s2.py @@ -74,10 +74,10 @@ def monthly_synthesis_inference(sources, sources_scales, pad, ts, savedmodel_dir # Setup TensorFlowModelServe system.set_env_var("OTB_TF_NSOURCES", str(len(sources))) - infer = pyotb.App("TensorflowModelServe", execute=False) + infer_params = {} # Setup BandMath for post processing - bm = pyotb.App("BandMath", execute=False) + bm_params = {} mask_expr = "0" # Inputs @@ -93,7 +93,7 @@ def monthly_synthesis_inference(sources, sources_scales, pad, ts, savedmodel_dir if placeholder in sources_scales: src_rfield = int(rfield / sources_scales[placeholder]) - infer.set_parameters({get_key("il"): [source]}) + infer_params.update({get_key("il"): [source]}) # Update post processing BandMath expression if placeholder != 'dem' and '20m' not in placeholder: @@ -101,33 +101,34 @@ def monthly_synthesis_inference(sources, sources_scales, pad, ts, savedmodel_dir n_channels = pyotb.get_nbchannels(source) mask_expr += "||" mask_expr += "&&".join(["im{}b{}=={}".format(k + 1, b, nodatavalue) for b in range(1, 1 + n_channels)]) - bm.set_parameters(il=[source]) + bm_params.update({'il': source}) k += 1 - infer.set_parameters({get_key("rfieldx"): src_rfield, - get_key("rfieldy"): src_rfield, - get_key("placeholder"): placeholder}) + infer_params.update({get_key("rfieldx"): src_rfield, + get_key("rfieldy"): src_rfield, + get_key("placeholder"): placeholder}) # Model - infer.set_parameters({"model.dir": savedmodel_dir, "model.fullyconv": "on", - "output.names": [padded_tensor_name(out_tensor, pad)], - "output.efieldx": efield, "output.efieldy": efield, - "optim.tilesizex": efield, "optim.tilesizey": efield, - "optim.disabletiling": 1}) - infer.Execute() + infer_params.update({"model.dir": savedmodel_dir, "model.fullyconv": True, + "output.names": [padded_tensor_name(out_tensor, pad)], + "output.efieldx": efield, "output.efieldy": efield, + "optim.tilesizex": efield, "optim.tilesizey": efield, + "optim.disabletiling": True}) + infer = pyotb.TensorflowModelServe(infer_params) # For ESA Sentinel-2, remove potential zeros the network may have introduced in the valid parts of the image if out_pixeltype == otbApplication.ImagePixelType_uint16: - n_channels = pyotb.get_nbchannels(infer.out) + n_channels = pyotb.get_nbchannels(infer) exp = ';'.join([f'(im1b{b}<=1 ? 1 : im1b{b})' for b in range(1, 1 + n_channels)]) - rmzeros = pyotb.App("BandMathX", il=[infer.out], exp=exp) + rmzeros = pyotb.App("BandMathX", il=[infer], exp=exp) rmzeros.SetParameterOutputImagePixelType("out", out_pixeltype) else: rmzeros = infer # Mask for post processing mask_expr += "?0:255" - bm.set_parameters(exp=mask_expr) + bm_params.update({'exp': mask_expr}) + bm = pyotb.BandMath(bm_params) # Closing post processing mask to remove small groups of NoData pixels closing = pyotb.App("BinaryMorphologicalOperation", bm, filter="closing", foreval=255, structype="box", diff --git a/decloud/production/monthly_synthesis_processor_s2s1.py b/decloud/production/monthly_synthesis_processor_s2s1.py index 10adc0aea0faeaa99f090f9b81d2ca1c243da2d2..36ec22a5706e1b11467c452feb9373d814d165ca 100644 --- a/decloud/production/monthly_synthesis_processor_s2s1.py +++ b/decloud/production/monthly_synthesis_processor_s2s1.py @@ -74,10 +74,10 @@ def monthly_synthesis_inference(sources, sources_scales, pad, ts, savedmodel_dir # Setup TensorFlowModelServe system.set_env_var("OTB_TF_NSOURCES", str(len(sources))) - infer = pyotb.App("TensorflowModelServe", execute=False) + infer_params = {} # Setup BandMath for post processing - bm = pyotb.App("BandMath", execute=False) + bm_params = {} mask_expr = "0" # Inputs @@ -93,7 +93,7 @@ def monthly_synthesis_inference(sources, sources_scales, pad, ts, savedmodel_dir if placeholder in sources_scales: src_rfield = int(rfield / sources_scales[placeholder]) - infer.set_parameters({get_key("il"): [source]}) + infer_params.update({get_key("il"): [source]}) # Update post processing BandMath expression if placeholder != 'dem' and '20m' not in placeholder: @@ -101,20 +101,20 @@ def monthly_synthesis_inference(sources, sources_scales, pad, ts, savedmodel_dir n_channels = pyotb.get_nbchannels(source) mask_expr += "||" mask_expr += "&&".join(["im{}b{}=={}".format(k + 1, b, nodatavalue) for b in range(1, 1 + n_channels)]) - bm.set_parameters(il=[source]) + bm_params.update({'il': [source]}) k += 1 - infer.set_parameters({get_key("rfieldx"): src_rfield, - get_key("rfieldy"): src_rfield, - get_key("placeholder"): placeholder}) + infer_params.update({get_key("rfieldx"): src_rfield, + get_key("rfieldy"): src_rfield, + get_key("placeholder"): placeholder}) # Model - infer.set_parameters({"model.dir": savedmodel_dir, "model.fullyconv": "on", - "output.names": [padded_tensor_name(out_tensor, pad)], - "output.efieldx": efield, "output.efieldy": efield, - "optim.tilesizex": efield, "optim.tilesizey": efield, - "optim.disabletiling": 1}) - infer.Execute() + infer_params.update({"model.dir": savedmodel_dir, "model.fullyconv": True, + "output.names": [padded_tensor_name(out_tensor, pad)], + "output.efieldx": efield, "output.efieldy": efield, + "optim.tilesizex": efield, "optim.tilesizey": efield, + "optim.disabletiling": True}) + infer = pyotb.TensorflowModelServe(infer_params) # For ESA Sentinel-2, remove potential zeros the network may have introduced in the valid parts of the image if out_pixeltype == otbApplication.ImagePixelType_uint16: @@ -127,7 +127,8 @@ def monthly_synthesis_inference(sources, sources_scales, pad, ts, savedmodel_dir # Mask for post processing mask_expr += "?0:255" - bm.set_parameters(exp=mask_expr) + bm_params.update({'exp': mask_expr}) + bm = pyotb.BandMath(bm_params) # Closing post processing mask to remove small groups of NoData pixels closing = pyotb.App("BinaryMorphologicalOperation", bm, filter="closing", foreval=255, structype="box", @@ -269,6 +270,7 @@ if __name__ == "__main__": """ return abs(s2_product.get_timestamp() - x.get_timestamp()) + input_s1_products.sort(key=_closest_date, reverse=True) input_s1_images_10m = [product.get_raster_10m() for product in input_s1_products] # creating a mosaic with the N closest S1 images diff --git a/setup.py b/setup.py index 579b3a50f2c658655b717362cbcfa6923123d22e..ec9843b927c4ad69034de0b3a72905f199fd3038 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ with open("README.md", "r", encoding="utf-8") as fh: setuptools.setup( name="decloud", - version="0.1", + version="1.3", author="Remi Cresson, Nicolas Narçon, Benjamin Commandre", author_email="remi.cresson@inrae.fr", description="Deep learning based reconstruction of optical time series using SAR imagery", diff --git a/tests/decloud_unittest.py b/tests/decloud_unittest.py index c97334050c40d6cec28be21c13b204e631c34e0a..669f7db9b4a209aac31e512657b01f26948f69b6 100644 --- a/tests/decloud_unittest.py +++ b/tests/decloud_unittest.py @@ -4,7 +4,7 @@ import os import subprocess import unittest import filecmp -import gdal +from osgeo import gdal import otbApplication as otb from abc import ABC from decloud.core.system import get_env_var, basename diff --git a/tests/dem_unittest.py b/tests/dem_unittest.py index e9c5639edcdb11e459d823153da3752b8be996f8..9804886876ac4a19c6be6a4aa0d30c32691945ad 100644 --- a/tests/dem_unittest.py +++ b/tests/dem_unittest.py @@ -15,7 +15,6 @@ class DEMTest(DecloudTest): "SENTINEL2B_20201026-103901-924_L2A_T31TEJ_C_V2-2_FRE_20m.tif"), "--output", "/tmp/dem_prepare.tif", "--tmp", "/tmp/DEM"]) - self.compare_raster_metadata("/tmp/dem_prepare.tif", self.get_path("baseline/PREPARE/DEM_PREPARE/T31TEJ.tif")) self.compare_images("/tmp/dem_prepare.tif", self.get_path("baseline/PREPARE/DEM_PREPARE/T31TEJ.tif")) diff --git a/tests/inference_unittest.py b/tests/inference_unittest.py index 5f310c4e019765a28e230f57325c07e6690c1429..316b9c486c90cc64cd75bea0c11f3f874215d59b 100644 --- a/tests/inference_unittest.py +++ b/tests/inference_unittest.py @@ -84,7 +84,6 @@ class InferenceTest(DecloudTest): self.assertTrue(system.file_exists(outpath)) self.compare_images(outpath, baseline_path) - self.compare_raster_metadata(outpath, baseline_path) def test_inference_with_generic_preprocessor(self): # Logger @@ -128,7 +127,6 @@ class InferenceTest(DecloudTest): self.assertTrue(system.file_exists(outpath)) self.compare_images(outpath, baseline_path) - self.compare_raster_metadata(outpath, baseline_path) if __name__ == '__main__': diff --git a/tests/sentinel1_prepare_unittest.py b/tests/sentinel1_prepare_unittest.py index 86d5e9e362eceab870af8b5b33cfe464d53e3395..ec402b835de79b23830e6f80402e86d7f9d95308 100644 --- a/tests/sentinel1_prepare_unittest.py +++ b/tests/sentinel1_prepare_unittest.py @@ -12,9 +12,6 @@ class Sentinel1PrepareTest(DecloudTest): sentinel1_prepare.main(["--input_s1_vh", self.get_path("inputs/THEIA/s1b_31TEJ_vh_DES_110_20201011t060008.tif"), "--out_s1_dir", "/tmp/s1_prepare"]) - self.compare_raster_metadata("/tmp/s1_prepare/s1b_31TEJ_vvvh_DES_110_20201011t060008_from-10to3dB.tif", - self.get_path("baseline/PREPARE/S1_PREPARE/T31TEJ/" - "s1b_31TEJ_vvvh_DES_110_20201011t060008_from-10to3dB.tif")) self.compare_images("/tmp/s1_prepare/s1b_31TEJ_vvvh_DES_110_20201011t060008_from-10to3dB.tif", self.get_path("baseline/PREPARE/S1_PREPARE/T31TEJ/" "s1b_31TEJ_vvvh_DES_110_20201011t060008_from-10to3dB.tif")) diff --git a/tests/sentinel2_prepare_unittest.py b/tests/sentinel2_prepare_unittest.py index 7896e6d61f962fb08786e8630f23df2174552166..3ade77c4d19bf7bcaa63cd97e2d82f46e6d4314b 100644 --- a/tests/sentinel2_prepare_unittest.py +++ b/tests/sentinel2_prepare_unittest.py @@ -13,11 +13,6 @@ class Sentinel2PrepareTest(DecloudTest): "-497_L2A_T31TEJ_C_V2-2"), "--out_s2_dir", "/tmp/s2_prepare"]) - self.compare_raster_metadata("/tmp/s2_prepare/T31TEJ/SENTINEL2B_20201012-105848-497_L2A_T31TEJ_C_V2-2/" - "SENTINEL2B_20201012-105848-497_L2A_T31TEJ_C_V2-2_FRE_10m.tif", - self.get_path("baseline/PREPARE/S2_PREPARE/T31TEJ/" - "SENTINEL2B_20201012-105848-497_L2A_T31TEJ_C_V2-2/" - "SENTINEL2B_20201012-105848-497_L2A_T31TEJ_C_V2-2_FRE_10m.tif")) self.compare_images("/tmp/s2_prepare/T31TEJ/SENTINEL2B_20201012-105848-497_L2A_T31TEJ_C_V2-2/" "SENTINEL2B_20201012-105848-497_L2A_T31TEJ_C_V2-2_FRE_10m.tif", self.get_path("baseline/PREPARE/S2_PREPARE/T31TEJ/"