From 7a4d8a183fe25a5bad4b15de6ce4fdb64af33688 Mon Sep 17 00:00:00 2001
From: "Daniel.Frisinghelli" <daniel.frisinghelli@eurac.edu>
Date: Thu, 7 Jan 2021 17:40:49 +0100
Subject: [PATCH] Implemented the interface to the ALCD dataset.

---
 pysegcnn/core/constants.py | 104 ++++++++++---------------------------
 pysegcnn/core/dataset.py   | 104 ++++++++++++++-----------------------
 pysegcnn/core/trainer.py   |  29 +++++------
 pysegcnn/main/config.py    |  35 ++++++++-----
 4 files changed, 102 insertions(+), 170 deletions(-)

diff --git a/pysegcnn/core/constants.py b/pysegcnn/core/constants.py
index 4161956..08a5a9c 100644
--- a/pysegcnn/core/constants.py
+++ b/pysegcnn/core/constants.py
@@ -105,24 +105,6 @@ class LabelMapping(dict):
         return np.array(list(self.items()))
 
 
-class Gdal2Numpy(enum.Enum):
-    """Data type mapping from gdal to numpy."""
-
-    Byte = np.uint8
-    UInt8 = np.uint8
-    Int8 = np.int8
-    UInt16 = np.uint16
-    Int16 = np.int16
-    UInt32 = np.uint32
-    Int32 = np.int32
-    Float32 = np.float32
-    Float64 = np.float64
-    CInt16 = np.complex64
-    CInt32 = np.complex64
-    CFloat32 = np.complex64
-    CFloat64 = np.complex64
-
-
 class SparcsLabels(Label):
     """Class labels of the `Sparcs`_ dataset.
 
@@ -153,67 +135,37 @@ class Cloud95Labels(Label):
     Cloud = 1, 'white'
 
 
-class ProSnowLabels(Label):
-    """Class labels of the ProSnow datasets."""
+class AlcdLabels(Label):
+    """Class labels of the `Alcd`_ dataset.
 
-    Cloud = 0, 'white'
-    Snow = 1, 'lightblue'
-    Snow_free = 2, 'sienna'
-    No_data = 3, 'black'
+    .. _Alcd:
+        https://zenodo.org/record/1460961#.XYCTRzYzaHt
 
+    """
 
-def map_labels(source, target):
-    """Map labels from a source domain to a target domain.
-
-    Parameters
-    ----------
-    source : :py:class:`enum.EnumMeta`
-        The source domain labels, i.e. the labels a model is trained with.
-    target : :py:class:`enum.EnumMeta`
-        The target domain labels, i.e. the labels of the dataset to apply the
-        model to.
+    No_data = 0, 'black'
+    Not_used = 1, 'black'
+    Cloud = 2, 'white'
+    Cirrus = 3, 'white'
+    Shadow = 4, 'grey'
+    Land = 5, 'forestgreen'
+    Water = 6, 'blue'
+    Snow = 7, 'lightblue'
 
-    Raises
-    ------
-    ValueError
-        Raised if no label mapping from ``source`` to ``target`` is defined.
 
-    Returns
-    -------
-    label_map : `dict` [`int`, `int`]
-        Dictionary with source labels as keys and corresponding target labels
-        as values.
+class Gdal2Numpy(enum.Enum):
+    """Data type mapping from gdal to numpy."""
 
-    """
-    # if source and target labels are equal, the label mapping is the
-    # identity
-    if source is target:
-        label_map = None
-
-    # mapping from Sparcs to ProSnow
-    elif source is SparcsLabels and target is ProSnowLabels:
-        # label transformation mapping
-        label_map = {
-            # Shadow = Snow Free
-            SparcsLabels.Shadow.id: ProSnowLabels.Snow_free.id,
-            # Shadow ow = Snow Free
-            SparcsLabels.Shadow_over_water.id: ProSnowLabels.Snow_free.id,
-            # Water = Snow Free
-            SparcsLabels.Water.id: ProSnowLabels.Snow_free.id,
-            # Snow = Snow
-            SparcsLabels.Snow.id: ProSnowLabels.Snow.id,
-            # Land = Snow Free
-            SparcsLabels.Land.id: ProSnowLabels.Snow_free.id,
-            # Cloud = Cloud
-            SparcsLabels.Cloud.id: ProSnowLabels.Cloud.id,
-            # Flooded = Snow Free
-            SparcsLabels.Flooded.id: ProSnowLabels.Snow_free.id,
-            # No data = No data
-            SparcsLabels.No_data.id: ProSnowLabels.No_data.id
-            }
-
-    else:
-        raise ValueError('Unknown label mapping from {} to {}'.
-                         format(source.__name__, target.__name__))
-
-    return label_map
+    Byte = np.uint8
+    UInt8 = np.uint8
+    Int8 = np.int8
+    UInt16 = np.uint16
+    Int16 = np.int16
+    UInt32 = np.uint32
+    Int32 = np.int32
+    Float32 = np.float32
+    Float64 = np.float64
+    CInt16 = np.complex64
+    CInt32 = np.complex64
+    CFloat32 = np.complex64
+    CFloat64 = np.complex64
diff --git a/pysegcnn/core/dataset.py b/pysegcnn/core/dataset.py
index da0c02d..fdd0df9 100644
--- a/pysegcnn/core/dataset.py
+++ b/pysegcnn/core/dataset.py
@@ -28,6 +28,7 @@ import csv
 import enum
 import itertools
 import logging
+import pathlib
 
 # externals
 import numpy as np
@@ -35,9 +36,9 @@ import torch
 from torch.utils.data import Dataset
 
 # locals
-from pysegcnn.core.constants import (Landsat8, Sentinel2, Label, SparcsLabels,
-                                     Cloud95Labels, ProSnowLabels,
-                                     MultiSpectralSensor)
+from pysegcnn.core.constants import (MultiSpectralSensor, Landsat8, Sentinel2,
+                                     Label, SparcsLabels, Cloud95Labels,
+                                     AlcdLabels)
 from pysegcnn.core.utils import (img2np, is_divisible, tile_topleft_corner,
                                  parse_landsat_scene, parse_sentinel2_scene)
 
@@ -54,7 +55,7 @@ class ImageDataset(Dataset):
 
     Attributes
     ----------
-    root_dir : `str`
+    root_dir : `str` or :py:class:`pathlib.Path`
         The root directory, path to the dataset.
     use_bands : `list` [`str`]
         List of the spectral bands to use during model training.
@@ -118,7 +119,7 @@ class ImageDataset(Dataset):
 
         Parameters
         ----------
-        root_dir : `str`
+        root_dir : `str` or :py:class:`pathlib.Path`
             The root directory, path to the dataset.
         use_bands : `list` [`str`], optional
             A list of the spectral bands to use. The default is `[]`.
@@ -160,7 +161,7 @@ class ImageDataset(Dataset):
         super().__init__()
 
         # dataset configuration
-        self.root = root_dir
+        self.root = pathlib.Path(root_dir)
         self.use_bands = use_bands
         self.tile_size = tile_size
         self.pad = pad
@@ -222,7 +223,7 @@ class ImageDataset(Dataset):
             LOGGER.info('Adding label "No data" with value={} to ground truth.'
                         .format(self.cval))
         else:
-            self._labels.pop(self.cval)
+            # self._labels.pop(self.cval)
             self.cval = 0
 
         # remove labels to merge from dataset instance labels
@@ -812,8 +813,7 @@ class StandardEoDataset(ImageDataset):
 
             # check if the current directory name matches a scene identifier
             scene = self.parse_scene_id(dirpath)
-
-            if scene is not None:
+            if scene:
 
                 # get the date of the current scene
                 date = scene['date']
@@ -897,7 +897,7 @@ class SparcsDataset(StandardEoDataset):
 
     @staticmethod
     def get_sensor():
-        """Landsat 8 bands of the Sparcs dataset.
+        """Landsat-8 bands of the Sparcs dataset.
 
         Returns
         -------
@@ -921,7 +921,7 @@ class SparcsDataset(StandardEoDataset):
 
     @staticmethod
     def parse_scene_id(scene_id):
-        """Parse Sparcs scene identifiers (Landsat 8).
+        """Parse Sparcs scene identifiers (Landsat-8).
 
         Parameters
         ----------
@@ -938,8 +938,16 @@ class SparcsDataset(StandardEoDataset):
         return parse_landsat_scene(scene_id)
 
 
-class ProSnowDataset(StandardEoDataset):
-    """Class for the ProSnow datasets."""
+class AlcdDataset(StandardEoDataset):
+    """Class for the `Alcd`_ dataset by `Baetens et al. (2019)`_.
+
+    .. _Alcd:
+        https://zenodo.org/record/1460961#.XYCTRzYzaHt
+
+    .. _Baetens et al. (2019):
+        https://www.mdpi.com/2072-4292/11/4/433
+
+    """
 
     def __init__(self, root_dir, use_bands=[], tile_size=None, pad=False,
                  gt_pattern='(.*)gt\\.tif', sort=False, seed=0, transforms=[],
@@ -948,9 +956,21 @@ class ProSnowDataset(StandardEoDataset):
         super().__init__(root_dir, use_bands, tile_size, pad, gt_pattern,
                          sort, seed, transforms, merge_labels)
 
+    @staticmethod
+    def get_size():
+        """Image size of the Alcd dataset.
+
+        Returns
+        -------
+        size : `tuple`
+            The image size (height, width).
+
+        """
+        return (1830, 1830)
+
     @staticmethod
     def get_sensor():
-        """Sentinel 2 bands of the ProSnow datasets.
+        """Sentinel-2 bands of the Alcd dataset.
 
         Returns
         -------
@@ -962,7 +982,7 @@ class ProSnowDataset(StandardEoDataset):
 
     @staticmethod
     def get_labels():
-        """Class labels of the ProSnow datasets.
+        """Class labels of the Alcd dataset.
 
         Returns
         -------
@@ -970,11 +990,11 @@ class ProSnowDataset(StandardEoDataset):
             The class labels.
 
         """
-        return ProSnowLabels
+        return AlcdLabels
 
     @staticmethod
     def parse_scene_id(scene_id):
-        """Parse ProSnow scene identifiers (Sentinel 2).
+        """Parse Alcd scene identifiers (Sentinel-2).
 
         Parameters
         ----------
@@ -991,57 +1011,12 @@ class ProSnowDataset(StandardEoDataset):
         return parse_sentinel2_scene(scene_id)
 
 
-class ProSnowGarmisch(ProSnowDataset):
-    """Class for the ProSnow Garmisch dataset."""
-
-    def __init__(self, root_dir, use_bands=[], tile_size=None, pad=False,
-                 gt_pattern='(.*)gt\\.tif', sort=False, seed=0, transforms=[],
-                 merge_labels={}):
-        # initialize super class StandardEoDataset
-        super().__init__(root_dir, use_bands, tile_size, pad, gt_pattern,
-                         sort, seed, transforms, merge_labels)
-
-    @staticmethod
-    def get_size():
-        """Image size of the ProSnow Garmisch dataset.
-
-        Returns
-        -------
-        size : `tuple`
-            The image size (height, width).
-
-        """
-        return (615, 543)
-
-
-class ProSnowObergurgl(ProSnowDataset):
-    """Class for the ProSnow Obergurgl dataset."""
-
-    def __init__(self, root_dir, use_bands=[], tile_size=None, pad=False,
-                 gt_pattern='(.*)gt\\.tif', sort=False, seed=0, transforms=[],
-                 merge_labels={}):
-        # initialize super class StandardEoDataset
-        super().__init__(root_dir, use_bands, tile_size, pad, gt_pattern,
-                         sort, seed, transforms, merge_labels)
-
-    @staticmethod
-    def get_size():
-        """Image size of the ProSnow Obergurgl dataset.
-
-        Returns
-        -------
-        size : `tuple`
-            The image size (height, width).
-
-        """
-        return (310, 270)
-
-
 class Cloud95Dataset(ImageDataset):
     """Class for the `Cloud-95`_ dataset by `Mohajerani & Saeedi (2020)`_.
 
     .. _Cloud-95:
         https://github.com/SorourMo/95-Cloud-An-Extension-to-38-Cloud-Dataset
+
     .. _Mohajerani & Saeedi (2020):
         https://arxiv.org/abs/2001.08768
 
@@ -1218,6 +1193,5 @@ class SupportedDatasets(enum.Enum):
     """Names and corresponding classes of the implemented datasets."""
 
     Sparcs = SparcsDataset
+    Alcd = AlcdDataset
     Cloud95 = Cloud95Dataset
-    Garmisch = ProSnowGarmisch
-    Obergurgl = ProSnowObergurgl
diff --git a/pysegcnn/core/trainer.py b/pysegcnn/core/trainer.py
index a9c5d6c..1d894c6 100644
--- a/pysegcnn/core/trainer.py
+++ b/pysegcnn/core/trainer.py
@@ -50,7 +50,6 @@ from pysegcnn.core.layers import Conv2dSame
 from pysegcnn.core.logging import log_conf
 from pysegcnn.core.graphics import (plot_loss, plot_confusion_matrix,
                                     plot_sample, Animate)
-from pysegcnn.core.constants import map_labels
 from pysegcnn.main.config import HERE, DRIVE_PATH
 
 # module level logger
@@ -1370,23 +1369,23 @@ class NetworkInference(BaseConfig):
         """
         return self.trg_ds.dataset.labels
 
-    @property
-    def label_map(self):
-        """Label mapping from the source to the target domain.
+    # @property
+    # def label_map(self):
+    #     """Label mapping from the source to the target domain.
 
-        See :py:func:`pysegcnn.core.constants.map_labels`.
+    #     See :py:func:`pysegcnn.core.constants.map_labels`.
 
-        Returns
-        -------
-        label_map : `dict` [`int`, `int`]
-            Dictionary with source labels as keys and corresponding target
-            labels as values.
+    #     Returns
+    #     -------
+    #     label_map : `dict` [`int`, `int`]
+    #         Dictionary with source labels as keys and corresponding target
+    #         labels as values.
 
-        """
-        # check whether the source domain labels are the same as the target
-        # domain labels
-        return map_labels(self.src_ds.get_labels(),
-                          self.trg_ds.dataset.get_labels())
+    #     """
+    #     # check whether the source domain labels are the same as the target
+    #     # domain labels
+    #     return map_labels(self.src_ds.get_labels(),
+    #                       self.trg_ds.dataset.get_labels())
 
     @property
     def source_is_target(self):
diff --git a/pysegcnn/main/config.py b/pysegcnn/main/config.py
index 9068f6b..bab13b0 100644
--- a/pysegcnn/main/config.py
+++ b/pysegcnn/main/config.py
@@ -19,27 +19,33 @@ License
 # -*- coding: utf-8 -*-
 
 # builtins
-import os
+import pathlib
 
 # from pysegcnn.core.transforms import Augment, FlipLr, FlipUd, Noise
 
 # path to this file
-HERE = os.path.abspath(os.path.dirname(__file__))
+HERE = pathlib.Path(__file__).resolve().parent
 
 # path to the datasets on the current machine
-DRIVE_PATH = 'C:/Eurac/2020/CCISNOW/_Datasets/'
-# DRIVE_PATH = '/mnt/CEPH_PROJECTS/cci_snow/dfrisinghelli/_Datasets/'
+DRIVE_PATH = pathlib.Path('C:/Eurac/2020/CCISNOW/_Datasets/')
+# DRIVE_PATH = pathlib.Path('/mnt/CEPH_PROJECTS/cci_snow/dfrisinghelli/_Datasets/')
 
 # name and paths to the datasets
-DATASETS = {'Sparcs': os.path.join(DRIVE_PATH, 'Sparcs'),
-            'Cloud95': os.path.join(DRIVE_PATH, 'Cloud95/Training'),
-            'Garmisch': os.path.join(DRIVE_PATH, 'ProSnow/Garmisch')}
+DATASETS = {'Sparcs': DRIVE_PATH.joinpath('Sparcs'),
+            'Alcd': DRIVE_PATH.joinpath('Alcd/60m')
+            }
 
 # name of the source domain dataset
 SRC_DS = 'Sparcs'
 
 # name of the target domain dataset
-TRG_DS = 'Garmisch'
+TRG_DS = 'Alcd'
+
+# spectral bands to use for training
+BANDS = ['red', 'green', 'blue', 'nir', 'swir1', 'swir2']
+
+# tile size of a single sample
+TILE_SIZE = None
 
 # the source dataset configuration dictionary
 src_ds_config = {
@@ -63,11 +69,11 @@ src_ds_config = {
     # or [], which corresponds to using all available bands
     # IMPORTANT: the list of bands should be equal for the source and target
     #            domains, when using any sort of transfer learning
-    'bands': ['red', 'green', 'blue', 'nir', 'swir1', 'swir2'],
+    'bands': BANDS,
 
     # define the size of the network input
     # if None, the size will default to the size of a scene
-    'tile_size': 128,
+    'tile_size': TILE_SIZE,
 
     # whether to central pad the scenes with a constant value
     # if True, padding is used if the scenes are not evenly divisible into
@@ -146,14 +152,15 @@ src_ds_config = {
 trg_ds_config = {
     'dataset_name': TRG_DS,
     'root_dir': DATASETS[TRG_DS],
-    'gt_pattern': '(.*)class\\.img',
-    'bands': ['red', 'green', 'blue', 'nir', 'swir1', 'swir2'],
-    'tile_size': 128,
+    'gt_pattern': '(.*)Labels\\.tif',
+    'bands': BANDS,
+    'tile_size': TILE_SIZE,
     'pad': True,
     'seed': 0,
     'sort': True,
     'transforms': [],
-    'merge_labels': {}
+    'merge_labels': {'Cirrus': 'Cloud',
+                     'Not_used': 'No_data'}
 }
 
 # the source dataset split configuration dictionary
-- 
GitLab