コード例 #1
0
def test_denormaliser_maximum():
    """Confirm that input equal to +1 is denormalised to x_max"""
    x_min = 10
    x_max = 20
    normaliser = Normaliser(x_min=x_min, x_max=x_max)
    normalised_input = normaliser.denormalise(1.)
    expected_output = x_max
    assert normalised_input == expected_output
コード例 #2
0
def test_denormaliser_middle():
    """Confirm that input equal to 0 is denormalised to half way between x_min and x_max"""
    x_min = 10
    x_max = 20
    normaliser = Normaliser(x_min=x_min, x_max=x_max)
    normalised_input = normaliser.denormalise(0.)
    expected_output = (x_min + x_max) / 2.
    assert normalised_input == expected_output
コード例 #3
0
def test_normaliser_out_of_range():
    """Confirm that input greater than +1 is denormalised to a value proportionately greater than x_max"""
    x_min = 10
    x_max = 20
    normaliser = Normaliser(x_min=x_min, x_max=x_max)
    input = 2.
    normalised_input = normaliser.denormalise(input)
    expected_output = x_max + (input - 1) * 0.5 * (x_max - x_min)
    assert normalised_input == expected_output
コード例 #4
0
 def _initialise_output_normalisation(self):
     self.norm_output = Normaliser(x_min=0.00000385066859365878,
                                   x_max=0.522417054644758)
コード例 #5
0
 def _initialise_output_normalisation(self):
     self.norm_output = Normaliser(x_min=0.000181230723879,
                                   x_max=0.999638214714515)
コード例 #6
0
 def _initialise_output_normalisation(self):
     self.norm_output = Normaliser(x_min=0.000153013463222,
                                   x_max=0.977135096979553)
コード例 #7
0
 def _initialise_normalisation(self):
     """Initialise the Normalisers to preprocess inputs according to the expected/validated minimums & maximums"""
     self.norm_b3 = Normaliser(x_min=0., x_max=0.253061520471542)
     self.norm_b4 = Normaliser(x_min=0., x_max=0.290393577911328)
     self.norm_b5 = Normaliser(x_min=0., x_max=0.305398915248555)
     self.norm_b6 = Normaliser(x_min=0.006637972542253,
                               x_max=0.608900395797889)
     self.norm_b7 = Normaliser(x_min=0.013972727018939,
                               x_max=0.753827384322927)
     self.norm_b8a = Normaliser(x_min=0.026690138082061,
                                x_max=0.782011770669178)
     self.norm_b11 = Normaliser(x_min=0.016388074192258,
                                x_max=0.493761397883092)
     self.norm_b12 = Normaliser(x_min=0., x_max=0.493025984460231)
     self.norm_cos_view_zenith = Normaliser(x_min=0.918595400582046,
                                            x_max=0.99999999999139)
     self.norm_cos_sun_zenith = Normaliser(x_min=0.342022871159208,
                                           x_max=0.936206429175402)
     self.norm_cos_rel_azimuth = Normaliser(x_min=-0.999999982118044,
                                            x_max=0.999999998910077)
     self._initialise_output_normalisation()
コード例 #8
0
class S2BiophysicalCalculator(ABC):
    def __init__(self):
        """Calculates BioPhysical property values from Sentinel-2 imagery as per the S2 toolbox products
        """
        self._initialise_normalisation()
        self._initialise_network()

    def _initialise_normalisation(self):
        """Initialise the Normalisers to preprocess inputs according to the expected/validated minimums & maximums"""
        self.norm_b3 = Normaliser(x_min=0., x_max=0.253061520471542)
        self.norm_b4 = Normaliser(x_min=0., x_max=0.290393577911328)
        self.norm_b5 = Normaliser(x_min=0., x_max=0.305398915248555)
        self.norm_b6 = Normaliser(x_min=0.006637972542253,
                                  x_max=0.608900395797889)
        self.norm_b7 = Normaliser(x_min=0.013972727018939,
                                  x_max=0.753827384322927)
        self.norm_b8a = Normaliser(x_min=0.026690138082061,
                                   x_max=0.782011770669178)
        self.norm_b11 = Normaliser(x_min=0.016388074192258,
                                   x_max=0.493761397883092)
        self.norm_b12 = Normaliser(x_min=0., x_max=0.493025984460231)
        self.norm_cos_view_zenith = Normaliser(x_min=0.918595400582046,
                                               x_max=0.99999999999139)
        self.norm_cos_sun_zenith = Normaliser(x_min=0.342022871159208,
                                              x_max=0.936206429175402)
        self.norm_cos_rel_azimuth = Normaliser(x_min=-0.999999982118044,
                                               x_max=0.999999998910077)
        self._initialise_output_normalisation()

    @abstractmethod
    def _initialise_output_normalisation(self):
        """Define the normalisation parameters on the output to return outputs into the natural range of values

        MUST be overridden in concrete child classes.
        """
        self.norm_output = None

    @abstractmethod
    def _initialise_network(self):
        """Define the network structure & parameterisation

        MUST be overridden in concrete child classes
        """
        self.neuron_1 = None
        self.neuron_2 = None
        self.neuron_3 = None
        self.neuron_4 = None
        self.neuron_5 = None
        self.neuron_6 = None
        self.network = None

    def run(self, input_arr: np.ndarray, band_sequence: List[str] = DEFAULT_BAND_SEQUENCE, validate: bool = True) -> \
            Union[float, np.float]:
        """Run the calculator on an input array

        By default, the calculator expects only the following bands to be passed in the sequence below:

        - B03
        - B04
        - B05
        - B06
        - B07
        - B8a
        - B11
        - B12
        - COS_VIEW_ZENITH
        - COS_SUN_ZENITH
        - COS_REL_AZIMUTH

        If band values are to be passed in a different set or sequence, the band_sequence parameter must be passed with
        band names (matching those above) for each element in the input array.
        eg. ["extra_band_1", "COS_SUN_ZENITH", "B03", "B04", ..., "COS_REL_AZIMUTH", "extra_band_2"]

        :param input_arr: Input values for the calculator to use
        :param band_sequence: Names of bands included in the input array (names must match those used above for the required bands)
        :param validate: Flag for whether or not to apply validation ranges to the inputs
        :return: Scalar estimate of the biophysical property
        """
        if not band_sequence == DEFAULT_BAND_SEQUENCE:
            band_idxs = [
                band_sequence.index(elem) for elem in DEFAULT_BAND_SEQUENCE
            ]
            ordered_arr = np.array([input_arr[idx] for idx in band_idxs])
        else:
            ordered_arr = input_arr
        if validate:
            ordered_arr = self._validate(ordered_arr)
        normalised_arr = self._normalise(ordered_arr)
        y_norm = self._compute(normalised_arr)
        y = self.norm_output.denormalise(y_norm)
        return y

    def _validate(self, input_arr: np.ndarray) -> np.ndarray:
        """Validate input band values against the 'definition domain for inputs'

        NB. We validate against the min & max input values, but we do NOT check that inputs lie in valid cells of the
        convex hull

        :param input_arr: Band values to be validated
        :return: Band values after passing through validation. ValueError is raised if any values fail.
        """
        validation_ranges = self.VALIDATION_RANGES
        for band_index, band_name in enumerate(validation_ranges):
            band_value = input_arr[band_index]
            if validation_ranges.get(band_name).get(
                    "min") <= band_value <= validation_ranges.get(
                        band_name).get("max"):
                continue
            else:
                raise ValueError(
                    f"Band {band_name} failed validation because it is expected to fall in the range [{validation_ranges.get(band_name).get('min')}, {validation_ranges.get(band_name).get('max')}]"
                )
        return input_arr

    def _normalise(self, band_values: np.ndarray) -> np.ndarray:
        """Normalise input band values to predefined ranges before passing to the neural network for calculation.

        :param band_values: Band values to be normalised
        :return: Normalised band values
        """
        return np.array([
            self.norm_b3.normalise(band_values[0]),
            self.norm_b4.normalise(band_values[1]),
            self.norm_b5.normalise(band_values[2]),
            self.norm_b6.normalise(band_values[3]),
            self.norm_b7.normalise(band_values[4]),
            self.norm_b8a.normalise(band_values[5]),
            self.norm_b11.normalise(band_values[6]),
            self.norm_b12.normalise(band_values[7]),
            self.norm_cos_view_zenith.normalise(band_values[8]),
            self.norm_cos_sun_zenith.normalise(band_values[9]),
            self.norm_cos_rel_azimuth.normalise(band_values[10]),
        ])

    def _compute(self, normalised_arr: np.ndarray) -> np.float:
        """Calculates normalised FAPAR from normalised input band values

        :param normalised_arr: Normalised band values
        :return: Normalised FAPAR estimate
        """
        return self.network.forward(normalised_arr)
コード例 #9
0
def test_denormaliser_minimum():
    """Confirm that input equal to -1. is denormalised to x_min"""
    normaliser = Normaliser(x_min=10, x_max=20)
    denormalised_input = normaliser.denormalise(-1.)
    expected_output = 10
    assert denormalised_input == expected_output
コード例 #10
0
def test_normaliser_minimum():
    """Confirm that input equal to x_min is normalised to -1"""
    normaliser = Normaliser(x_min=10, x_max=20)
    normalised_input = normaliser.normalise(10)
    expected_output = -1.
    assert normalised_input == expected_output
コード例 #11
0
def test_normaliser_out_of_range():
    """Confirm that input above x_max is normalised to >1"""
    normaliser = Normaliser(x_min=10, x_max=20)
    normalised_input = normaliser.normalise(25)
    expected_output = 2.
    assert normalised_input == expected_output
コード例 #12
0
def test_normaliser_middle():
    """Confirm that input equal to midway between x_min & x_max is normalised to 0."""
    normaliser = Normaliser(x_min=10, x_max=20)
    normalised_input = normaliser.normalise(15)
    expected_output = 0.
    assert normalised_input == expected_output