diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 271b1daf7274419b0577a9dc37a862c8b3eab5ab..7e7f213e8fc2792a60cb1cbf6e45b0bac49190ed 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,6 +3,9 @@ image: gitlab-registry.irstea.fr/remi.cresson/otbtf:2.4-cpu-basic-testing variables: OTB_BUILD: /src/otb/build/OTB/build # Local OTB build directory OTBTF_SRC: /src/otbtf # Local OTBTF source directory + OTB_TEST_DIR: $OTB_BUILD/Testing/Temporary # OTB testing directory + ARTIFACT_TEST_DIR: $CI_PROJECT_DIR/testing + CRC_BOOK_TMP: /tmp/crc_book_tests_tmp workflow: rules: @@ -13,13 +16,17 @@ stages: - Build - Static Analysis - Test + - Applications Test .update_otbtf_src: &update_otbtf_src - sudo rm -rf $OTBTF_SRC && sudo ln -s $PWD $OTBTF_SRC # Replace local OTBTF source directory .compile_otbtf: &compile_otbtf - cd $OTB_BUILD && sudo make install -j$(nproc --all) # Rebuild OTB with new OTBTF sources - + +.install_pytest: &install_pytest + - pip3 install pytest pytest-cov pytest-order # Install pytest stuff + before_script: - *update_otbtf_src @@ -60,21 +67,47 @@ ctest: stage: Test script: - *compile_otbtf - - sudo rm -rf $OTB_BUILD/Testing/Temporary/* # Empty testing temporary folder (old files here) + - sudo rm -rf $OTB_TEST_DIR/* # Empty testing temporary folder (old files here) - cd $OTB_BUILD/ && sudo ctest -L OTBTensorflow # Run ctest after_script: - - cp -r $OTB_BUILD/Testing/Temporary $CI_PROJECT_DIR/testing # Copy artifacts (they must be in $CI_PROJECT_DIR) + - cp -r $OTB_TEST_DIR $ARTIFACT_TEST_DIR artifacts: paths: - - testing/*.* + - $ARTIFACT_TEST_DIR/*.* expire_in: 1 week when: on_failure +.applications_test_base: + stage: Applications Test + rules: + # Only for MR targeting 'develop' branch because applications tests are slow + - if: $CI_MERGE_REQUEST_ID && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'develop' + artifacts: + when: on_failure + paths: + - $CI_PROJECT_DIR/report_*.xml + - $ARTIFACT_TEST_DIR/*.* + expire_in: 1 week + +crc_book: + extends: .applications_test_base + script: + - *compile_otbtf + - *install_pytest + - cd $CI_PROJECT_DIR + - mkdir -p $CRC_BOOK_TMP + - TMPDIR=$CRC_BOOK_TMP \ + DATADIR=$CI_PROJECT_DIR/test/data \ + python -m pytest --junitxml=$CI_PROJECT_DIR/report_tutorial.xml $OTBTF_SRC/test/tutorial_unittest.py + after_script: + - mkdir -p $ARTIFACT_TEST_DIR + - cp $CRC_BOOK_TMP/*.* $ARTIFACT_TEST_DIR/ + sr4rs: - stage: Test + extends: .applications_test_base script: - *compile_otbtf - - pip3 install pytest pytest-cov + - *install_pytest - cd $CI_PROJECT_DIR - wget -O sr4rs_sentinel2_bands4328_france2020_savedmodel.zip https://nextcloud.inrae.fr/s/boabW9yCjdpLPGX/download/sr4rs_sentinel2_bands4328_france2020_savedmodel.zip @@ -85,8 +118,4 @@ sr4rs: - git clone https://github.com/remicres/sr4rs.git - export PYTHONPATH=$PYTHONPATH:$PWD/sr4rs - python -m pytest --junitxml=$CI_PROJECT_DIR/report_sr4rs.xml $OTBTF_SRC/test/sr4rs_unittest.py - artifacts: - when: on_failure - paths: - - $CI_PROJECT_DIR/report_sr4rs.xml - expire_in: 1 week + diff --git a/test/data/RF_model_from_deep_features_map.tif b/test/data/RF_model_from_deep_features_map.tif new file mode 100644 index 0000000000000000000000000000000000000000..d41d832c4d1e4f9848636d76f45cac26a1fdce15 Binary files /dev/null and b/test/data/RF_model_from_deep_features_map.tif differ diff --git a/test/data/amsterdam_labels_A.tif b/test/data/amsterdam_labels_A.tif new file mode 100644 index 0000000000000000000000000000000000000000..d07a0e0cfbcfad1f6b1b887ddf2a9470687c58be Binary files /dev/null and b/test/data/amsterdam_labels_A.tif differ diff --git a/test/data/amsterdam_labels_B.tif b/test/data/amsterdam_labels_B.tif new file mode 100644 index 0000000000000000000000000000000000000000..b7af54a4c573aa9bbf1c7407e7dac9597a5a19e2 Binary files /dev/null and b/test/data/amsterdam_labels_B.tif differ diff --git a/test/data/amsterdam_patches_A.tif b/test/data/amsterdam_patches_A.tif new file mode 100644 index 0000000000000000000000000000000000000000..e8d2e70ece62fd8c1e20820b74c0edf2f16f80dd Binary files /dev/null and b/test/data/amsterdam_patches_A.tif differ diff --git a/test/data/amsterdam_patches_B.tif b/test/data/amsterdam_patches_B.tif new file mode 100644 index 0000000000000000000000000000000000000000..d6b20fc81a85a57f09af319aa74af7e5e7cf76dc Binary files /dev/null and b/test/data/amsterdam_patches_B.tif differ diff --git a/test/data/classif_model1.tif b/test/data/classif_model1.tif new file mode 100644 index 0000000000000000000000000000000000000000..a681cfd6a22867778b5b5452ab881f0352d567ba Binary files /dev/null and b/test/data/classif_model1.tif differ diff --git a/test/data/classif_model2.tif b/test/data/classif_model2.tif new file mode 100644 index 0000000000000000000000000000000000000000..9b23ede1e26a72a52c843038f56b7b762e4f0d1b Binary files /dev/null and b/test/data/classif_model2.tif differ diff --git a/test/data/classif_model3_fcn.tif b/test/data/classif_model3_fcn.tif new file mode 100644 index 0000000000000000000000000000000000000000..8109d0d7432b321055c9562d613710603dbd4b99 Binary files /dev/null and b/test/data/classif_model3_fcn.tif differ diff --git a/test/data/classif_model3_pb.tif b/test/data/classif_model3_pb.tif new file mode 100644 index 0000000000000000000000000000000000000000..028c5cefaf60344b80aeafe7c6bc03b19034f206 Binary files /dev/null and b/test/data/classif_model3_pb.tif differ diff --git a/test/data/classif_model4.tif b/test/data/classif_model4.tif new file mode 100644 index 0000000000000000000000000000000000000000..b87fe1e3f61613301b2d496fdd0ff41e5c66da53 Binary files /dev/null and b/test/data/classif_model4.tif differ diff --git a/test/data/fake_spot6.jp2 b/test/data/fake_spot6.jp2 new file mode 100644 index 0000000000000000000000000000000000000000..6464ca60b7a86fc18b0be664885466ab064c6262 Binary files /dev/null and b/test/data/fake_spot6.jp2 differ diff --git a/test/data/outvec_A.gpkg b/test/data/outvec_A.gpkg new file mode 100644 index 0000000000000000000000000000000000000000..87c8301d562985ceb5bc97f0c899cfd4c07de294 Binary files /dev/null and b/test/data/outvec_A.gpkg differ diff --git a/test/data/outvec_B.gpkg b/test/data/outvec_B.gpkg new file mode 100644 index 0000000000000000000000000000000000000000..78578a3a204cb393478905492dd8b859aaa5691e Binary files /dev/null and b/test/data/outvec_B.gpkg differ diff --git a/test/data/s2_10m_labels_A.tif b/test/data/s2_10m_labels_A.tif new file mode 100644 index 0000000000000000000000000000000000000000..1af82324acce9247ecb86283c05d52f0a616a3de Binary files /dev/null and b/test/data/s2_10m_labels_A.tif differ diff --git a/test/data/s2_10m_labels_B.tif b/test/data/s2_10m_labels_B.tif new file mode 100644 index 0000000000000000000000000000000000000000..de91615723f14adb16ac024fce92b1be32f4103b Binary files /dev/null and b/test/data/s2_10m_labels_B.tif differ diff --git a/test/data/s2_10m_patches_A.tif b/test/data/s2_10m_patches_A.tif new file mode 100644 index 0000000000000000000000000000000000000000..c0567ff9282fb6628a1d9200fd9d63143b1c2487 Binary files /dev/null and b/test/data/s2_10m_patches_A.tif differ diff --git a/test/data/s2_10m_patches_B.tif b/test/data/s2_10m_patches_B.tif new file mode 100644 index 0000000000000000000000000000000000000000..056fe19ee93c6a892bb78f9174e014d0377ba3a9 Binary files /dev/null and b/test/data/s2_10m_patches_B.tif differ diff --git a/test/data/s2_20m_patches_A.tif b/test/data/s2_20m_patches_A.tif new file mode 100644 index 0000000000000000000000000000000000000000..2f299c2f5b1b4eed702d0c7d9bb542169b760434 Binary files /dev/null and b/test/data/s2_20m_patches_A.tif differ diff --git a/test/data/s2_20m_patches_B.tif b/test/data/s2_20m_patches_B.tif new file mode 100644 index 0000000000000000000000000000000000000000..afd4ac9775c191983af6cbea1da82943bace7be2 Binary files /dev/null and b/test/data/s2_20m_patches_B.tif differ diff --git a/test/data/s2_20m_stack.jp2 b/test/data/s2_20m_stack.jp2 new file mode 100644 index 0000000000000000000000000000000000000000..c518a4ba71845802ebd6f443d18f626da22e5175 Binary files /dev/null and b/test/data/s2_20m_stack.jp2 differ diff --git a/test/data/s2_labels_A.tif b/test/data/s2_labels_A.tif new file mode 100644 index 0000000000000000000000000000000000000000..c4451320fad48a811d7b9a1b221502013d4a822f Binary files /dev/null and b/test/data/s2_labels_A.tif differ diff --git a/test/data/s2_labels_B.tif b/test/data/s2_labels_B.tif new file mode 100644 index 0000000000000000000000000000000000000000..2b90eb45f97cf3e65045fcca00b95ec8aa7c0a77 Binary files /dev/null and b/test/data/s2_labels_B.tif differ diff --git a/test/data/s2_patches_A.tif b/test/data/s2_patches_A.tif new file mode 100644 index 0000000000000000000000000000000000000000..4f338e312214e89bf85e4aafe4b20588918dcc6f Binary files /dev/null and b/test/data/s2_patches_A.tif differ diff --git a/test/data/s2_patches_B.tif b/test/data/s2_patches_B.tif new file mode 100644 index 0000000000000000000000000000000000000000..022d525e31db3713f4f10264411a0cfbbe76cf74 Binary files /dev/null and b/test/data/s2_patches_B.tif differ diff --git a/test/data/s2_stack.jp2 b/test/data/s2_stack.jp2 new file mode 100644 index 0000000000000000000000000000000000000000..cd5864ee2583441036aeec6bce96eec6b0533ebd Binary files /dev/null and b/test/data/s2_stack.jp2 differ diff --git a/test/data/terrain_truth_epsg32654_A.tif b/test/data/terrain_truth_epsg32654_A.tif new file mode 100644 index 0000000000000000000000000000000000000000..87a9a74bd6a337cac61559e2682b53676fecbd66 Binary files /dev/null and b/test/data/terrain_truth_epsg32654_A.tif differ diff --git a/test/data/terrain_truth_epsg32654_B.tif b/test/data/terrain_truth_epsg32654_B.tif new file mode 100644 index 0000000000000000000000000000000000000000..2114099b6a815fd3fd5d74449488e8e4ed98ca19 Binary files /dev/null and b/test/data/terrain_truth_epsg32654_B.tif differ diff --git a/test/sr4rs_unittest.py b/test/sr4rs_unittest.py index fbb921f8451cc83b3fd7b9e9e90bf61755511eca..89c3945f153304d04d35c23ef34513cf668120bc 100644 --- a/test/sr4rs_unittest.py +++ b/test/sr4rs_unittest.py @@ -4,8 +4,7 @@ import unittest import os from pathlib import Path -import gdal -import otbApplication as otb +import test_utils def command_train_succeed(extra_opts=""): @@ -54,21 +53,7 @@ class SR4RSv1Test(unittest.TestCase): command += "--output '{}?&box=256:256:512:512'".format(out_img) os.system(command) - nbchannels_reconstruct = gdal.Open(out_img).RasterCount - nbchannels_baseline = gdal.Open(baseline).RasterCount - - self.assertTrue(nbchannels_reconstruct == nbchannels_baseline) - - for i in range(1, 1 + nbchannels_baseline): - comp = otb.Registry.CreateApplication('CompareImages') - comp.SetParameterString('ref.in', baseline) - comp.SetParameterInt('ref.channel', i) - comp.SetParameterString('meas.in', out_img) - comp.SetParameterInt('meas.channel', i) - comp.Execute() - mae = comp.GetParameterFloat('mae') - - self.assertTrue(mae < 0.01) + self.assertTrue(test_utils.compare(baseline, out_img)) if __name__ == '__main__': diff --git a/test/test_utils.py b/test/test_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..c07301e91bb2eab4a808d53beaa46396c3ea82fc --- /dev/null +++ b/test/test_utils.py @@ -0,0 +1,56 @@ +import otbApplication +import os + + +def get_nb_of_channels(raster): + """ + Return the number of channels in the input raster + :param raster: raster filename (str) + :return the number of channels in the image (int) + """ + info = otbApplication.Registry.CreateApplication("ReadImageInfo") + info.SetParameterString("in", raster) + info.ExecuteAndWriteOutput() + return info.GetParameterInt('numberbands') + + +def compare(raster1, raster2, tol=0.01): + """ + Return True if the two rasters have the same contents in each bands + :param raster1: raster 1 filename (str) + :param raster2: raster 2 filename (str) + :param tol: tolerance (float) + """ + n_bands1 = get_nb_of_channels(raster1) + n_bands2 = get_nb_of_channels(raster2) + if n_bands1 != n_bands2: + print("The images have not the same number of channels") + return False + + for i in range(1, 1 + n_bands1): + comp = otbApplication.Registry.CreateApplication('CompareImages') + comp.SetParameterString('ref.in', raster1) + comp.SetParameterInt('ref.channel', i) + comp.SetParameterString('meas.in', raster2) + comp.SetParameterInt('meas.channel', i) + comp.Execute() + mae = comp.GetParameterFloat('mae') + if mae > tol: + print("The images have not the same content in channel {} " + "(Mean average error: {})".format(i, mae)) + return False + return True + + +def resolve_paths(filename, var_list): + """ + Retrieve environment variables in paths + :param filename: file name + :params var_list: variable list + :return filename with retrieved environment variables + """ + new_filename = filename + for var in var_list: + new_filename = new_filename.replace("${}".format(var), os.environ[var]) + print("Resolve filename...\n\tfilename: {}, \n\tnew filename: {}".format(filename, new_filename)) + return new_filename diff --git a/test/tutorial_unittest.py b/test/tutorial_unittest.py new file mode 100644 index 0000000000000000000000000000000000000000..7934862f348326de13a11eef4c81a4fe6512cec6 --- /dev/null +++ b/test/tutorial_unittest.py @@ -0,0 +1,513 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import pytest +import unittest +import os +from pathlib import Path +import test_utils + +INFERENCE_MAE_TOL = 10.0 # Dummy value: we don't really care of the mae value but rather the image size etc + + +def resolve_paths(path): + """ + Resolve a path with the environment variables + """ + return test_utils.resolve_paths(path, var_list=["TMPDIR", "DATADIR"]) + + +def run_command(command): + """ + Run a command + :param command: the command to run + """ + full_command = resolve_paths(command) + print("Running command: \n\t {}".format(full_command)) + os.system(full_command) + + +def run_command_and_test_exist(command, file_list): + """ + :param command: the command to run (str) + :param file_list: list of files to check + :return True or False + """ + run_command(command) + print("Checking if files exist...") + for file in file_list: + print("\t{}".format(file)) + path = Path(resolve_paths(file)) + if not path.is_file(): + print("File {} does not exist!".format(file)) + return False + print("\tOk") + return True + + +def run_command_and_compare(command, to_compare_dict, tol=0.01): + """ + :param command: the command to run (str) + :param to_compare_dict: a dict of {baseline1: output1, ..., baselineN: outputN} + :param tol: tolerance (float) + :return True or False + """ + + run_command(command) + for baseline, output in to_compare_dict.items(): + if not test_utils.compare(resolve_paths(baseline), resolve_paths(output), tol): + print("Baseline {} and output {} differ.".format(baseline, output)) + return False + return True + + +class TutorialTest(unittest.TestCase): + + @pytest.mark.order(1) + def test_sample_selection(self): + self.assertTrue( + run_command_and_test_exist( + command="otbcli_LabelImageSampleSelection " + "-inref $DATADIR/terrain_truth_epsg32654_A.tif " + "-nodata 255 " + "-outvec $TMPDIR/outvec_A.gpkg", + file_list=["$TMPDIR/outvec_A.gpkg"])) + self.assertTrue( + run_command_and_test_exist( + command="otbcli_LabelImageSampleSelection " + "-inref $DATADIR/terrain_truth_epsg32654_B.tif " + "-nodata 255 " + "-outvec $TMPDIR/outvec_B.gpkg", + file_list=["$TMPDIR/outvec_B.gpkg"])) + + @pytest.mark.order(2) + def test_patches_extraction(self): + self.assertTrue( + run_command_and_compare( + command="otbcli_PatchesExtraction " + "-source1.il $DATADIR/s2_stack.jp2 " + "-source1.out $TMPDIR/s2_patches_A.tif " + "-source1.patchsizex 16 " + "-source1.patchsizey 16 " + "-vec $TMPDIR/outvec_A.gpkg " + "-field class " + "-outlabels $TMPDIR/s2_labels_A.tif", + to_compare_dict={"$DATADIR/s2_patches_A.tif": "$TMPDIR/s2_patches_A.tif", + "$DATADIR/s2_labels_A.tif": "$TMPDIR/s2_labels_A.tif"})) + self.assertTrue( + run_command_and_compare( + command="otbcli_PatchesExtraction " + "-source1.il $DATADIR/s2_stack.jp2 " + "-source1.out $TMPDIR/s2_patches_B.tif " + "-source1.patchsizex 16 " + "-source1.patchsizey 16 " + "-vec $TMPDIR/outvec_B.gpkg " + "-field class " + "-outlabels $TMPDIR/s2_labels_B.tif", + to_compare_dict={"$DATADIR/s2_patches_B.tif": "$TMPDIR/s2_patches_B.tif", + "$DATADIR/s2_labels_B.tif": "$TMPDIR/s2_labels_B.tif"})) + + @pytest.mark.order(3) + def test_generate_model1(self): + run_command("git clone https://github.com/remicres/otbtf_tutorials_resources.git " + "$TMPDIR/otbtf_tuto_repo") + self.assertTrue( + run_command_and_test_exist( + command="python $TMPDIR/otbtf_tuto_repo/01_patch_based_classification/models/create_model1.py " + "$TMPDIR/model1", + file_list=["$TMPDIR/model1/saved_model.pb"])) + + @pytest.mark.order(4) + def test_model1_train(self): + self.assertTrue( + run_command_and_test_exist( + command="otbcli_TensorflowModelTrain " + "-training.source1.il $DATADIR/s2_patches_A.tif " + "-training.source1.patchsizex 16 " + "-training.source1.patchsizey 16 " + "-training.source1.placeholder x " + "-training.source2.il $DATADIR/s2_labels_A.tif " + "-training.source2.patchsizex 1 " + "-training.source2.patchsizey 1 " + "-training.source2.placeholder y " + "-model.dir $TMPDIR/model1 " + "-training.targetnodes optimizer " + "-training.epochs 10 " + "-validation.mode class " + "-validation.source1.il $DATADIR/s2_patches_B.tif " + "-validation.source1.name x " + "-validation.source2.il $DATADIR/s2_labels_B.tif " + "-validation.source2.name prediction " + "-model.saveto $TMPDIR/model1/variables/variables", + file_list=["$TMPDIR/model1/variables/variables.index"] + ) + ) + + @pytest.mark.order(5) + def test_model1_inference_pb(self): + self.assertTrue( + run_command_and_compare( + command="otbcli_TensorflowModelServe " + "-source1.il $DATADIR/s2_stack.jp2 " + "-source1.rfieldx 16 " + "-source1.rfieldy 16 " + "-source1.placeholder x " + "-model.dir $TMPDIR/model1 " + "-output.names prediction " + "-out \"$TMPDIR/classif_model1.tif?&box=4000:4000:1000:1000\" uint8", + to_compare_dict={"$DATADIR/classif_model1.tif": "$TMPDIR/classif_model1.tif"}, + tol=INFERENCE_MAE_TOL)) + + @pytest.mark.order(6) + def test_model1_inference_fcn(self): + self.assertTrue( + run_command_and_compare( + command="otbcli_TensorflowModelServe " + "-source1.il $DATADIR/s2_stack.jp2 " + "-source1.rfieldx 16 " + "-source1.rfieldy 16 " + "-source1.placeholder x " + "-model.dir $TMPDIR/model1 " + "-output.names prediction " + "-model.fullyconv on " + "-output.spcscale 4 " + "-out \"$TMPDIR/classif_model1.tif?&box=1000:1000:256:256\" uint8", + to_compare_dict={"$DATADIR/classif_model1.tif": "$TMPDIR/classif_model1.tif"}, + tol=INFERENCE_MAE_TOL)) + + @pytest.mark.order(7) + def test_rf_sampling(self): + self.assertTrue( + run_command_and_test_exist( + command="otbcli_SampleExtraction " + "-in $DATADIR/s2_stack.jp2 " + "-vec $TMPDIR/outvec_A.gpkg " + "-field class " + "-out $TMPDIR/pixelvalues_A.gpkg", + file_list=["$TMPDIR/pixelvalues_A.gpkg"])) + self.assertTrue( + run_command_and_test_exist( + command="otbcli_SampleExtraction " + "-in $DATADIR/s2_stack.jp2 " + "-vec $TMPDIR/outvec_B.gpkg " + "-field class " + "-out $TMPDIR/pixelvalues_B.gpkg", + file_list=["$TMPDIR/pixelvalues_B.gpkg"])) + + @pytest.mark.order(8) + def test_rf_training(self): + self.assertTrue( + run_command_and_test_exist( + command="otbcli_TrainVectorClassifier " + "-io.vd $TMPDIR/pixelvalues_A.gpkg " + "-valid.vd $TMPDIR/pixelvalues_B.gpkg " + "-feat value_0 value_1 value_2 value_3 " + "-cfield class " + "-classifier rf " + "-io.out $TMPDIR/randomforest_model.yaml ", + file_list=["$TMPDIR/randomforest_model.yaml"])) + + @pytest.mark.order(9) + def test_generate_model2(self): + self.assertTrue( + run_command_and_test_exist( + command="python $TMPDIR/otbtf_tuto_repo/01_patch_based_classification/models/create_model2.py " + "$TMPDIR/model2", + file_list=["$TMPDIR/model2/saved_model.pb"])) + + @pytest.mark.order(10) + def test_model2_train(self): + self.assertTrue( + run_command_and_test_exist( + command="otbcli_TensorflowModelTrain " + "-training.source1.il $DATADIR/s2_patches_A.tif " + "-training.source1.patchsizex 16 " + "-training.source1.patchsizey 16 " + "-training.source1.placeholder x " + "-training.source2.il $DATADIR/s2_labels_A.tif " + "-training.source2.patchsizex 1 " + "-training.source2.patchsizey 1 " + "-training.source2.placeholder y " + "-model.dir $TMPDIR/model2 " + "-training.targetnodes optimizer " + "-training.epochs 10 " + "-validation.mode class " + "-validation.source1.il $DATADIR/s2_patches_B.tif " + "-validation.source1.name x " + "-validation.source2.il $DATADIR/s2_labels_B.tif " + "-validation.source2.name prediction " + "-model.saveto $TMPDIR/model2/variables/variables", + file_list=["$TMPDIR/model2/variables/variables.index"])) + + @pytest.mark.order(11) + def test_model2_inference_fcn(self): + self.assertTrue( + run_command_and_compare(command="otbcli_TensorflowModelServe " + "-source1.il $DATADIR/s2_stack.jp2 " + "-source1.rfieldx 16 " + "-source1.rfieldy 16 " + "-source1.placeholder x " + "-model.dir $TMPDIR/model2 " + "-model.fullyconv on " + "-output.names prediction " + "-out \"$TMPDIR/classif_model2.tif?&box=4000:4000:1000:1000\" uint8", + to_compare_dict={"$DATADIR/classif_model2.tif": "$TMPDIR/classif_model2.tif"}, + tol=INFERENCE_MAE_TOL)) + + @pytest.mark.order(12) + def test_model2rf_train(self): + self.assertTrue( + run_command_and_test_exist( + command="otbcli_TrainClassifierFromDeepFeatures " + "-source1.il $DATADIR/s2_stack.jp2 " + "-source1.rfieldx 16 " + "-source1.rfieldy 16 " + "-source1.placeholder x " + "-model.dir $TMPDIR/model2 " + "-model.fullyconv on " + "-optim.tilesizex 999999 " + "-optim.tilesizey 128 " + "-output.names features " + "-vd $TMPDIR/outvec_A.gpkg " + "-valid $TMPDIR/outvec_B.gpkg " + "-sample.vfn class " + "-sample.bm 0 " + "-classifier rf " + "-out $TMPDIR/RF_model_from_deep_features.yaml", + file_list=["$TMPDIR/RF_model_from_deep_features.yaml"])) + + @pytest.mark.order(13) + def test_model2rf_inference(self): + self.assertTrue( + run_command_and_compare( + command="otbcli_ImageClassifierFromDeepFeatures " + "-source1.il $DATADIR/s2_stack.jp2 " + "-source1.rfieldx 16 " + "-source1.rfieldy 16 " + "-source1.placeholder x " + "-deepmodel.dir $TMPDIR/model2 " + "-deepmodel.fullyconv on " + "-output.names features " + "-model $TMPDIR/RF_model_from_deep_features.yaml " + "-out \"$TMPDIR/RF_model_from_deep_features_map.tif?&box=4000:4000:1000:1000\" uint8", + to_compare_dict={ + "$DATADIR/RF_model_from_deep_features_map.tif": "$TMPDIR/RF_model_from_deep_features_map.tif"}, + tol=INFERENCE_MAE_TOL)) + + @pytest.mark.order(14) + def test_patch_extraction_20m(self): + self.assertTrue( + run_command_and_compare( + command="OTB_TF_NSOURCES=2 otbcli_PatchesExtraction " + "-source1.il $DATADIR/s2_20m_stack.jp2 " + "-source1.patchsizex 8 " + "-source1.patchsizey 8 " + "-source1.out $TMPDIR/s2_20m_patches_A.tif " + "-source2.il $DATADIR/s2_stack.jp2 " + "-source2.patchsizex 16 " + "-source2.patchsizey 16 " + "-source2.out $TMPDIR/s2_10m_patches_A.tif " + "-vec $TMPDIR/outvec_A.gpkg " + "-field class " + "-outlabels $TMPDIR/s2_10m_labels_A.tif uint8", + to_compare_dict={"$DATADIR/s2_10m_labels_A.tif": "$TMPDIR/s2_10m_labels_A.tif", + "$DATADIR/s2_10m_patches_A.tif": "$TMPDIR/s2_10m_patches_A.tif", + "$DATADIR/s2_20m_patches_A.tif": "$TMPDIR/s2_20m_patches_A.tif"})) + self.assertTrue( + run_command_and_compare( + command="OTB_TF_NSOURCES=2 otbcli_PatchesExtraction " + "-source1.il $DATADIR/s2_20m_stack.jp2 " + "-source1.patchsizex 8 " + "-source1.patchsizey 8 " + "-source1.out $TMPDIR/s2_20m_patches_B.tif " + "-source2.il $DATADIR/s2_stack.jp2 " + "-source2.patchsizex 16 " + "-source2.patchsizey 16 " + "-source2.out $TMPDIR/s2_10m_patches_B.tif " + "-vec $TMPDIR/outvec_B.gpkg " + "-field class " + "-outlabels $TMPDIR/s2_10m_labels_B.tif uint8", + to_compare_dict={"$DATADIR/s2_10m_labels_B.tif": "$TMPDIR/s2_10m_labels_B.tif", + "$DATADIR/s2_10m_patches_B.tif": "$TMPDIR/s2_10m_patches_B.tif", + "$DATADIR/s2_20m_patches_B.tif": "$TMPDIR/s2_20m_patches_B.tif"})) + + @pytest.mark.order(15) + def test_generate_model3(self): + self.assertTrue( + run_command_and_test_exist( + command="python $TMPDIR/otbtf_tuto_repo/01_patch_based_classification/models/create_model3.py " + "$TMPDIR/model3", + file_list=["$TMPDIR/model3/saved_model.pb"])) + + @pytest.mark.order(16) + def test_model3_train(self): + self.assertTrue( + run_command_and_test_exist( + command="OTB_TF_NSOURCES=2 otbcli_TensorflowModelTrain " + "-training.source1.il $DATADIR/s2_20m_patches_A.tif " + "-training.source1.patchsizex 8 " + "-training.source1.patchsizey 8 " + "-training.source1.placeholder x1 " + "-training.source2.il $DATADIR/s2_10m_patches_A.tif " + "-training.source2.patchsizex 16 " + "-training.source2.patchsizey 16 " + "-training.source2.placeholder x2 " + "-training.source3.il $DATADIR/s2_10m_labels_A.tif " + "-training.source3.patchsizex 1 " + "-training.source3.patchsizey 1 " + "-training.source3.placeholder y " + "-model.dir $TMPDIR/model3 " + "-training.targetnodes optimizer " + "-training.epochs 10 " + "-validation.mode class " + "-validation.source1.il $DATADIR/s2_20m_patches_B.tif " + "-validation.source1.name x1 " + "-validation.source2.il $DATADIR/s2_10m_patches_B.tif " + "-validation.source2.name x2 " + "-validation.source3.il $DATADIR/s2_10m_labels_B.tif " + "-validation.source3.name prediction " + "-model.saveto $TMPDIR/model3/variables/variables", + file_list=["$TMPDIR/model3/variables/variables.index"])) + + @pytest.mark.order(17) + def test_model3_inference_pb(self): + self.assertTrue( + run_command_and_compare( + command= + "OTB_TF_NSOURCES=2 otbcli_TensorflowModelServe " + "-source1.il $DATADIR/s2_20m_stack.jp2 " + "-source1.rfieldx 8 " + "-source1.rfieldy 8 " + "-source1.placeholder x1 " + "-source2.il $DATADIR/s2_stack.jp2 " + "-source2.rfieldx 16 " + "-source2.rfieldy 16 " + "-source2.placeholder x2 " + "-model.dir $TMPDIR/model3 " + "-output.names prediction " + "-out \"$TMPDIR/classif_model3_pb.tif?&box=2000:2000:500:500&gdal:co:compress=deflate\"", + to_compare_dict={"$DATADIR/classif_model3_pb.tif": "$TMPDIR/classif_model3_pb.tif"}, + tol=INFERENCE_MAE_TOL)) + + @pytest.mark.order(18) + def test_model3_inference_fcn(self): + self.assertTrue( + run_command_and_compare( + command= + "OTB_TF_NSOURCES=2 otbcli_TensorflowModelServe " + "-source1.il $DATADIR/s2_20m_stack.jp2 " + "-source1.rfieldx 8 " + "-source1.rfieldy 8 " + "-source1.placeholder x1 " + "-source2.il $DATADIR/s2_stack.jp2 " + "-source2.rfieldx 16 " + "-source2.rfieldy 16 " + "-source2.placeholder x2 " + "-model.dir $TMPDIR/model3 " + "-model.fullyconv on " + "-output.names prediction " + "-out \"$TMPDIR/classif_model3_fcn.tif?&box=2000:2000:500:500&gdal:co:compress=deflate\"", + to_compare_dict={"$DATADIR/classif_model3_fcn.tif": "$TMPDIR/classif_model3_fcn.tif"}, + tol=INFERENCE_MAE_TOL)) + + @pytest.mark.order(19) + def test_generate_model4(self): + self.assertTrue( + run_command_and_test_exist( + command="python $TMPDIR/otbtf_tuto_repo/02_semantic_segmentation/models/create_model4.py " + "$TMPDIR/model4", + file_list=["$TMPDIR/model4/saved_model.pb"])) + + @pytest.mark.order(20) + def test_patches_selection_semseg(self): + self.assertTrue( + run_command_and_test_exist( + command="otbcli_PatchesSelection " + "-in $DATADIR/fake_spot6.jp2 " + "-grid.step 64 " + "-grid.psize 64 " + "-outtrain $TMPDIR/outvec_A_semseg.gpkg " + "-outvalid $TMPDIR/outvec_B_semseg.gpkg", + file_list=["$TMPDIR/outvec_A_semseg.gpkg", + "$TMPDIR/outvec_B_semseg.gpkg"])) + + @pytest.mark.order(21) + def test_patch_extraction_semseg(self): + self.assertTrue( + run_command_and_compare( + command="OTB_TF_NSOURCES=2 otbcli_PatchesExtraction " + "-source1.il $DATADIR/fake_spot6.jp2 " + "-source1.patchsizex 64 " + "-source1.patchsizey 64 " + "-source1.out \"$TMPDIR/amsterdam_patches_A.tif?&gdal:co:compress=deflate\" " + "-source2.il $TMPDIR/otbtf_tuto_repo/02_semantic_segmentation/" + "amsterdam_dataset/terrain_truth/amsterdam_labelimage.tif " + "-source2.patchsizex 64 " + "-source2.patchsizey 64 " + "-source2.out \"$TMPDIR/amsterdam_labels_A.tif?&gdal:co:compress=deflate\" " + "-vec $TMPDIR/outvec_A_semseg.gpkg " + "-field id ", + to_compare_dict={"$DATADIR/amsterdam_labels_A.tif": "$TMPDIR/amsterdam_labels_A.tif", + "$DATADIR/amsterdam_patches_A.tif": "$TMPDIR/amsterdam_patches_A.tif"})) + self.assertTrue( + run_command_and_compare( + command="OTB_TF_NSOURCES=2 otbcli_PatchesExtraction " + "-source1.il $DATADIR/fake_spot6.jp2 " + "-source1.patchsizex 64 " + "-source1.patchsizey 64 " + "-source1.out \"$TMPDIR/amsterdam_patches_B.tif?&gdal:co:compress=deflate\" " + "-source2.il $TMPDIR/otbtf_tuto_repo/02_semantic_segmentation/" + "amsterdam_dataset/terrain_truth/amsterdam_labelimage.tif " + "-source2.patchsizex 64 " + "-source2.patchsizey 64 " + "-source2.out \"$TMPDIR/amsterdam_labels_B.tif?&gdal:co:compress=deflate\" " + "-vec $TMPDIR/outvec_B_semseg.gpkg " + "-field id ", + to_compare_dict={"$DATADIR/amsterdam_labels_B.tif": "$TMPDIR/amsterdam_labels_B.tif", + "$DATADIR/amsterdam_patches_B.tif": "$TMPDIR/amsterdam_patches_B.tif"})) + + @pytest.mark.order(22) + def test_model4_train(self): + self.assertTrue( + run_command_and_test_exist( + command="OTB_TF_NSOURCES=1 otbcli_TensorflowModelTrain " + "-training.source1.il $DATADIR/amsterdam_patches_A.tif " + "-training.source1.patchsizex 64 " + "-training.source1.patchsizey 64 " + "-training.source1.placeholder x " + "-training.source2.il $DATADIR/amsterdam_labels_A.tif " + "-training.source2.patchsizex 64 " + "-training.source2.patchsizey 64 " + "-training.source2.placeholder y " + "-model.dir $TMPDIR/model4 " + "-training.targetnodes optimizer " + "-training.epochs 10 " + "-validation.mode class " + "-validation.source1.il $DATADIR/amsterdam_patches_B.tif " + "-validation.source1.name x " + "-validation.source2.il $DATADIR/amsterdam_labels_B.tif " + "-validation.source2.name prediction " + "-model.saveto $TMPDIR/model4/variables/variables", + file_list=["$TMPDIR/model4/variables/variables.index"])) + + @pytest.mark.order(23) + def test_model4_inference(self): + self.assertTrue( + run_command_and_compare( + command= + "otbcli_TensorflowModelServe " + "-source1.il $DATADIR/fake_spot6.jp2 " + "-source1.rfieldx 64 " + "-source1.rfieldy 64 " + "-source1.placeholder x " + "-model.dir $TMPDIR/model4 " + "-model.fullyconv on " + "-output.names prediction_fcn " + "-output.efieldx 32 " + "-output.efieldy 32 " + "-out \"$TMPDIR/classif_model4.tif?&gdal:co:compress=deflate\" uint8", + to_compare_dict={"$DATADIR/classif_model4.tif": "$TMPDIR/classif_model4.tif"}, + tol=INFERENCE_MAE_TOL)) + + +if __name__ == '__main__': + unittest.main()