-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
31 changed files
with
1,535 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,37 @@ | ||
"""Top-level package for pylst.""" | ||
# __init__.py | ||
|
||
__author__ = """Azad Rasul""" | ||
__email__ = '[email protected]' | ||
__version__ = '0.0.1' | ||
# Importing specific functionalities from different modules within the package | ||
|
||
# Importing from the 'pylst' module | ||
from .pylst import emissivity | ||
|
||
# Importing visualization functions from the 'visualization' module | ||
from .visualization import ( | ||
plot_data, # Function to plot data | ||
show, # Function to show visualization | ||
rastshow, # Function to show raster visualization | ||
create_interactive_map, # Function to create interactive map | ||
histogram_equalization # Function for histogram equalization | ||
) | ||
|
||
# Importing spatial analysis functions from the 'spatial_analysis' module | ||
from .spatial_analysis.zonstat import calculate_zs # Zonal statistics | ||
from .spatial_analysis.changedet import analyze_images # Change Detection | ||
|
||
# Importing image handling classes and functions from the 'open' module | ||
from .open import ( | ||
Opener, # Class for opening image data | ||
remopener, # Class for opening remote image data | ||
download_images # Function to download image collection images | ||
) | ||
|
||
# Importing normalization function from the 'normalization' module | ||
from .normalization import nrs # Normalization Ratio Scale function | ||
|
||
# Importing machine learning functions from the 'ml' module | ||
from .ml import ( | ||
train_regression_model, # Function to train regression model | ||
train_classification_model, # Function to train classification model | ||
train_test_split, # Function to split data for training and testing | ||
accuracy_score # Function to compute accuracy score | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Importing the default algorithms for emissivity calculations | ||
from .emissivity import default_algorithms |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
import numpy as np | ||
|
||
from pylandtemp.utils import rescale_band, cavity_effect, fractional_vegetation_cover | ||
|
||
# Credit to the original author: Oladimeji Mudele (pylandtemp) | ||
# Original source: https://github.com/pylandtemp/pylandtemp | ||
|
||
class Emissivity: | ||
def __init__(self): | ||
"""Parent class for all emissivity methods. Contains general methods and attributes""" | ||
self.ndvi_min = -1 | ||
self.ndvi_max = 1 | ||
self.baresoil_ndvi_max = 0.2 | ||
self.vegatation_ndvi_min = 0.5 | ||
|
||
def __call__(self, **kwargs) -> np.ndarray: | ||
"""Computes the emissivity | ||
kwargs: | ||
**ndvi (np.ndarray): NDVI image | ||
**red_band (np.ndarray): Band 4 or Red band image. | ||
**mask (np.ndarray[bool]): Mask image. Output will have NaN value where mask is True. | ||
Returns: | ||
Tuple(np.ndarray, np.ndarray): Emissivity for bands 10 and 11 respectively | ||
""" | ||
if "ndvi" not in kwargs: | ||
raise ValueError("NDVI image is not provided") | ||
|
||
if "red_band" not in kwargs: | ||
raise ValueError("Band 4 (red band) image is not provided") | ||
|
||
self.ndvi = kwargs["ndvi"] | ||
self.red_band = kwargs["red_band"] | ||
|
||
if ( | ||
self.ndvi is not None | ||
and self.red_band is not None | ||
and self.ndvi.shape != self.red_band.shape | ||
): | ||
raise ValueError( | ||
"Input images (NDVI and Red band) must be of equal dimension" | ||
) | ||
|
||
emm_10, emm_11 = self._compute_emissivity() | ||
mask = emm_10 == 0 | ||
emm_10[mask] = np.nan | ||
if emm_11 is not None: | ||
emm_11[mask] = np.nan | ||
return emm_10, emm_11 | ||
|
||
def _compute_emissivity(self): | ||
"""Abstract method to be implemented by subclasses""" | ||
raise NotImplementedError("No concrete implementation of emissivity method yet") | ||
|
||
def _get_land_surface_mask(self): | ||
"""Return a mask for differnet land surface types""" | ||
mask_baresoil = (self.ndvi >= self.ndvi_min) & ( | ||
self.ndvi < self.baresoil_ndvi_max | ||
) | ||
mask_vegetation = (self.ndvi > self.vegatation_ndvi_min) & ( | ||
self.ndvi <= self.ndvi_max | ||
) | ||
mask_mixed = (self.ndvi >= self.baresoil_ndvi_max) & ( | ||
self.ndvi <= self.vegatation_ndvi_min | ||
) | ||
return { | ||
"baresoil": mask_baresoil, | ||
"vegetation": mask_vegetation, | ||
"mixed": mask_mixed, | ||
} | ||
|
||
def _get_landcover_mask_indices(self): | ||
"""Returns indices corresponding to the different landcover classes of of interest namely: | ||
vegetation, baresoil and mixed" | ||
""" | ||
masks = self._get_land_surface_mask() | ||
baresoil = np.where(masks["baresoil"]) | ||
vegetation = np.where(masks["vegetation"]) | ||
mixed = np.where(masks["mixed"]) | ||
return {"baresoil": baresoil, "vegetation": vegetation, "mixed": mixed} | ||
|
||
def _compute_fvc(self): | ||
# Returns the fractional vegegation cover from the NDVI image. | ||
return fractional_vegetation_cover(self.ndvi) | ||
|
||
|
||
class ComputeMonoWindowEmissivity(Emissivity): | ||
|
||
emissivity_soil_10 = 0.97 | ||
emissivity_veg_10 = 0.99 | ||
emissivity_soil_11 = None | ||
emissivity_veg_11 = None | ||
|
||
def _compute_emissivity(self) -> np.ndarray: | ||
emm = np.empty_like(self.ndvi) | ||
landcover_mask_indices = self._get_landcover_mask_indices() | ||
|
||
# Baresoil value assignment | ||
emm[landcover_mask_indices["baresoil"]] = self.emissivity_soil_10 | ||
# Vegetation value assignment | ||
emm[landcover_mask_indices["vegetation"]] = self.emissivity_veg_10 | ||
# Mixed value assignment | ||
emm[landcover_mask_indices["mixed"]] = ( | ||
0.004 | ||
* (((self.ndvi[landcover_mask_indices["mixed"]] - 0.2) / (0.5 - 0.2)) ** 2) | ||
) + 0.986 | ||
return emm, emm | ||
|
||
|
||
class ComputeEmissivityNBEM(Emissivity): | ||
""" | ||
Method references: | ||
1. Li, Tianyu, and Qingmin Meng. "A mixture emissivity analysis method for | ||
urban land surface temperature retrieval from Landsat 8 data." Landscape | ||
and Urban Planning 179 (2018): 63-71. | ||
2. Yu, Xiaolei, Xulin Guo, and Zhaocong Wu. "Land surface temperature retrieval | ||
from Landsat 8 TIRS—Comparison between radiative transfer equation-based method, | ||
split window algorithm and single channel method." Remote sensing 6.10 (2014): 9829-9852. | ||
""" | ||
|
||
emissivity_soil_10 = 0.9668 | ||
emissivity_veg_10 = 0.9863 | ||
emissivity_soil_11 = 0.9747 | ||
emissivity_veg_11 = 0.9896 | ||
|
||
def _compute_emissivity(self) -> np.ndarray: | ||
|
||
if self.red_band is None: | ||
raise ValueError( | ||
"Red band cannot be {} for this emissivity computation method".format( | ||
self.red_band | ||
) | ||
) | ||
|
||
self.red_band = rescale_band(self.red_band) | ||
landcover_mask_indices = self._get_landcover_mask_indices() | ||
fractional_veg_cover = self._compute_fvc() | ||
|
||
def calc_emissivity_for_band( | ||
image, | ||
emissivity_veg, | ||
emissivity_soil, | ||
cavity_effect, | ||
red_band_coeff_a=None, | ||
red_band_coeff_b=None, | ||
): | ||
image[landcover_mask_indices["baresoil"]] = red_band_coeff_a - ( | ||
red_band_coeff_b * self.red_band[landcover_mask_indices["baresoil"]] | ||
) | ||
|
||
image[landcover_mask_indices["mixed"]] = ( | ||
(emissivity_veg * fractional_veg_cover[landcover_mask_indices["mixed"]]) | ||
+ ( | ||
emissivity_soil | ||
* (1 - fractional_veg_cover[landcover_mask_indices["mixed"]]) | ||
) | ||
+ cavity_effect[landcover_mask_indices["mixed"]] | ||
) | ||
|
||
image[landcover_mask_indices["vegetation"]] = ( | ||
emissivity_veg + cavity_effect[landcover_mask_indices["vegetation"]] | ||
) | ||
return image | ||
|
||
emissivity_band_10 = np.empty_like(self.ndvi) | ||
emissivity_band_11 = np.empty_like(self.ndvi) | ||
frac_vegetation_cover = self._compute_fvc() | ||
|
||
cavity_effect_10 = cavity_effect( | ||
self.emissivity_veg_10, self.emissivity_soil_10, fractional_veg_cover | ||
) | ||
cavity_effect_11 = cavity_effect( | ||
self.emissivity_veg_11, self.emissivity_soil_11, fractional_veg_cover | ||
) | ||
|
||
emissivity_band_10 = calc_emissivity_for_band( | ||
emissivity_band_10, | ||
self.emissivity_veg_10, | ||
self.emissivity_soil_10, | ||
cavity_effect_10, | ||
red_band_coeff_a=0.973, | ||
red_band_coeff_b=0.047, | ||
) | ||
emissivity_band_11 = calc_emissivity_for_band( | ||
emissivity_band_11, | ||
self.emissivity_veg_11, | ||
self.emissivity_soil_11, | ||
cavity_effect_11, | ||
red_band_coeff_a=0.984, | ||
red_band_coeff_b=0.026, | ||
) | ||
return emissivity_band_10, emissivity_band_11 | ||
|
||
|
||
class ComputeEmissivityGopinadh(Emissivity): | ||
""" | ||
Method reference: | ||
Rongali, Gopinadh, et al. "Split-window algorithm for retrieval of land surface temperature | ||
using Landsat 8 thermal infrared data." Journal of Geovisualization and Spatial Analysis 2.2 | ||
(2018): 1-19. | ||
""" | ||
|
||
emissivity_soil_10 = 0.971 | ||
emissivity_veg_10 = 0.987 | ||
|
||
emissivity_soil_11 = 0.977 | ||
emissivity_veg_11 = 0.989 | ||
|
||
def _compute_emissivity(self) -> np.ndarray: | ||
|
||
fractional_veg_cover = self._compute_fvc() | ||
|
||
def calc_emissivity_for_band( | ||
image, emissivity_veg, emissivity_soil, fractional_veg_cover | ||
): | ||
emm = (emissivity_soil * (1 - fractional_veg_cover)) + ( | ||
emissivity_veg * fractional_veg_cover | ||
) | ||
return emm | ||
|
||
emissivity_band_10 = np.empty_like(self.ndvi) | ||
emissivity_band_10 = calc_emissivity_for_band( | ||
emissivity_band_10, | ||
self.emissivity_veg_10, | ||
self.emissivity_soil_10, | ||
fractional_veg_cover, | ||
) | ||
|
||
emissivity_band_11 = np.empty_like(self.ndvi) | ||
emissivity_band_11 = calc_emissivity_for_band( | ||
emissivity_band_11, | ||
self.emissivity_veg_11, | ||
self.emissivity_soil_11, | ||
fractional_veg_cover, | ||
) | ||
return emissivity_band_10, emissivity_band_11 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Import emissivity computation algorithms from the algorithms module | ||
from .algorithms import ( | ||
ComputeMonoWindowEmissivity, | ||
ComputeEmissivityNBEM, | ||
ComputeEmissivityGopinadh, | ||
) | ||
|
||
# Define a dictionary of default emissivity computation algorithms | ||
# Each key corresponds to a method name, and the value is the corresponding algorithm class | ||
default_algorithms = { | ||
"avdan": ComputeMonoWindowEmissivity, # Avdan Ugur et al, 2016 method | ||
"xiaolei": ComputeEmissivityNBEM, # Xiaolei Yu et al, 2014 method | ||
"gopinadh": ComputeEmissivityGopinadh, # Gopinadh Rongali et al, 2018 method | ||
} | ||
|
||
# This dictionary allows users to easily choose a specific algorithm by providing the method name. | ||
# For example, to use the Avdan Ugur et al, 2016 method, users can select "avdan" as the method. | ||
# The corresponding algorithm class (ComputeMonoWindowEmissivity) will be used for computation. | ||
# Users can extend this dictionary by adding more methods and their corresponding algorithm classes. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import numpy as np | ||
|
||
|
||
__all__ = [ | ||
"InvalidMaskError", | ||
"InputShapesNotEqual", | ||
"InvalidMethodRequested", | ||
] | ||
|
||
|
||
class InvalidMaskError(Exception): | ||
"""Invalid mask error""" | ||
|
||
pass | ||
|
||
|
||
class KeywordArgumentError(Exception): | ||
"""Required keyword argument missing""" | ||
|
||
pass | ||
|
||
|
||
class InputShapesNotEqual(Exception): | ||
"""Input images don't have the same shape""" | ||
|
||
pass | ||
|
||
|
||
class InvalidMethodRequested(Exception): | ||
"""Invalid method/algorithm requested""" | ||
|
||
pass | ||
|
||
|
||
def assert_required_keywords_provided(keywords, **kwargs): | ||
""" | ||
This method checks if all the required keyword arguments to complete a computation | ||
are provided in **kwargs | ||
Args: | ||
keywords ([list[str]], optional): Required keywords. | ||
Raises: | ||
KeywordArgumentError: custom exception | ||
""" | ||
for keyword in keywords: | ||
if keyword not in kwargs or kwargs[keyword] is None: | ||
message = ( | ||
f"Keyword argument {keyword} must be provided for this computation " | ||
) | ||
raise KeywordArgumentError(message) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# pylst/ml/__init__.py | ||
|
||
from .ml import train_regression_model | ||
from .ml import train_classification_model | ||
from .ml import train_test_split | ||
from .ml import accuracy_score | ||
|
Oops, something went wrong.