Skip to content

Commit

Permalink
Merge pull request #41 from CU-ESIIL/serbinsh5
Browse files Browse the repository at this point in the history
Updates
  • Loading branch information
serbinsh authored Jun 6, 2024
2 parents 73d43d0 + 27445a0 commit 1199622
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 30 deletions.
128 changes: 101 additions & 27 deletions notebooks/how_to/Calculate_spectral_vegetation_indices.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,32 @@
"\n",
"Below we will walk through how you calculate SVIs remote sensing data, and EMIT hyperspectral imagery in general, why you might select specific band combinations based on what you are studying, and a few different ways you can calculate SVIs using EMIT data, either in-line, defining functions, or using pre-built functions found in the provided [spectral_index.py](https://github.com/CU-ESIIL/HYR-SENSE/blob/main/tools/functions/spectral_index.py) file\n",
"\n",
"First lets take a moment to consider again what remote sensing imagery like those provided by EMIT can tell us about the underlying vegetation. "
"First, let's take a moment to consider again what remote sensing imagery like those provided by EMIT can tell us about the underlying vegetation. The internal structure and biochemistry of leaves (A) within a canopy control the optical signatures observed by remote sensing instrumentation (B). The amount of incident radiation that is reflected by, transmitted through, or absorbed by leaves within a canopy is regulated by these structural and biochemical properties of leaves. For example, leaf properties such as a thick cuticle layer, high wax, and/or a large amount of leaf hairs can significantly influence the amount of first-surface reflectance (that is the reflected light directly off the outer leaf layer that does not interact with the leaf interior), causing less solar radiation to penetrate into the leaf. The thickness of the mesophyll layer associated with other properties, such as thicker leaves, can cause higher degree of internal leaf scattering, less transmittance through the leaf, and higher absorption in some wavelengths. Importantly, the diffuse reflectance out of the leaf is that modified by internal leaf properties and contains useful for mapping functional traits (B). High spectral resolution measurements of leaves and plant canopies enable the indirect, non-contact measurement of key structural and chemical absorption features that are associated with the physiological and biochemical properties of plants (B)\n"
]
},
{
"cell_type": "markdown",
"id": "8988a84b-a77b-455c-b626-ac048f015850",
"metadata": {},
"source": [
"|![leaf_anatomy.jpg](../../images/leaf_anatomy_figure.jpg) |![spectral_signatures.jpg](../../images/spectral_signatures.jpg)|"
"**A)** ![leaf_anatomy.jpg](../../images/leaf_anatomy_figure.jpg) **B)** ![spectral_signatures.jpg](../../images/spectral_signatures.jpg)"
]
},
{
"cell_type": "markdown",
"id": "b9911dec-f068-4e87-b1b8-c3edc67b7497",
"metadata": {},
"source": [
"We can make use of all of the information contained within the emergent spectral signatures provided by vegetation. We can do this by directly utilizing the spectral profiles of a leaf or an EMIT pixel, or we can instead target specific wavebands provided by data like EMIT to calculate a spectral vegetation index (SVI). SVIs range widely in the wavelengths/bands, structure, and applications. This article provides some background information ([https://www.nature.com/articles/s41597-023-02096-0](https://www.nature.com/articles/s41597-023-02096-0)) but you can find comprehensive list of SVIs [here](https://www.indexdatabase.de/)\n",
"\n",
"\n",
"For more information, you can review these select articles and resources that discuss how leaf and canopy structure, leaf chemical properties, and stress can alter the spectral signatures we see with remote sensing data like those provided by EMIT and how SVIs provide us a way to easily probe the properties of vegetation remotely.\n",
"\n",
"[Sources of variability in canopy reflectance and the convergent properties of plants](https://doi.org/10.1111/j.1469-8137.2010.03536.x)\n",
"\n",
"[Retrieval of foliar information about plant pigment systems from high resolution spectroscopy](https://doi.org/10.1016/j.rse.2008.10.019)\n",
"\n",
"[Scaling Functional Traits from Leaves to Canopies](https://link.springer.com/chapter/10.1007/978-3-030-33157-3_3)\n"
]
},
{
Expand All @@ -110,15 +127,17 @@
"source": [
"**Calculate the Normalized Difference Vegetation Index (NDVI)**\n",
"\n",
"Here we calculate a simple yet powerful index, the normalized difference vegetation index (NDVI). NDVI has been used for over 40 years to study changes on the Earth's surface, specifically related to vegetation, stress, and agriculture. For more information on NDVI, you can explore this article from NASA: [https://earthobservatory.nasa.gov/features/MeasuringVegetation/measuring_vegetation_1.php](https://earthobservatory.nasa.gov/features/MeasuringVegetation/measuring_vegetation_1.php)'"
"Here we calculate a simple yet powerful index, the normalized difference vegetation index (NDVI). NDVI has been used for over 40 years to study changes on the Earth's surface, specifically related to vegetation, stress, and agriculture. For more information on NDVI, you can explore this article from NASA: [https://earthobservatory.nasa.gov/features/MeasuringVegetation/measuring_vegetation_1.php](https://earthobservatory.nasa.gov/features/MeasuringVegetation/measuring_vegetation_1.php)"
]
},
{
"cell_type": "markdown",
"id": "5d280305-0ae2-4df7-86cb-27dfc7ed8f19",
"metadata": {},
"source": [
"To calculate NDVI, we need to select which bands we want to include in the calculation. In general, NDVI is defined using a red and near-infrared band, so lets use a band centered at 650nm and another at 850nm, both squarely within the red and NIR wavelength range as shown in the example spectral graph above"
"To calculate NDVI, we need to select which bands we want to include in the calculation. In general, NDVI is defined using a red and near-infrared band, so lets use a band centered at 650nm and another at 850nm, both squarely within the red and NIR wavelength range as shown in the example spectral graph above\n",
"\n",
"The basic structure of the NDVI is: NDVI = (NIR−Red)/(NIR + Red)"
]
},
{
Expand All @@ -128,9 +147,13 @@
"metadata": {},
"outputs": [],
"source": [
"### To calculate NDVI, starting with an EMIT xarray image we can select out the two bands using the example below\n",
"\n",
"refl650 = ds_geo.sel(wavelengths=650, method='nearest')\n",
"refl850 = ds_geo.sel(wavelengths=850, method='nearest')\n",
"ndvi = (refl850-refl650)/(refl850+refl650)"
"ndvi = (refl850-refl650)/(refl850+refl650)\n",
"\n",
"# Note: The 'nearest' option allows you to define a specific wavelength you want to use and it will use the closest EMIT band to that wavelength"
]
},
{
Expand All @@ -140,6 +163,7 @@
"metadata": {},
"outputs": [],
"source": [
"### Now lets view the resulting NDVI image stretech to display between 0-1\n",
"ndvi.hvplot.image(cmap='viridis', geo=True, tiles='ESRI', aspect = 'equal', frame_width=720, clim=(0,1)).opts(title=\"NDVI Image\")"
]
},
Expand All @@ -162,16 +186,19 @@
"source": [
"def normalized_diff_demo(input_xarray = ds_geo, band1=650, band2=850):\n",
" \"\"\"\n",
" This function takes an input xarray image and calculates a NDVI image based on the selected bands. The assumption is the input image is an EMIT image in \n",
" xarray format prepared using the emit_xarray() function from emit_tools\n",
" This function takes an input orthorectified EMIT xarray image and calculates a normalized-difference spectral index \n",
" image based on the selected bands. The assumption is the input image is an EMIT image in xarray format \n",
" prepared using the emit_xarray() function from emit_tools.\n",
"\n",
" The function format is Band2-Band1 / Band2+Band1\n",
" The function provided in the traditional two-band normalized difference index format, i.e. NDI = Band2-Band1 / Band2+Band1\n",
"\n",
" param: input_xarray is the input EMIT xarray in memory\n",
" Function parameters:\n",
" param: input_xarray is the input EMIT xarray image currently located in memory; at present this will not accept \n",
" the full path to a file that hasn't been loaded into memory yet (TODO)\n",
" \n",
" param: band1 the EMIT band number to use for band 1\n",
" param: band3 the EMIT band number to use for band 3\n",
"\n",
" \n",
" param: band2 the EMIT band number to use for band 2\n",
" \"\"\"\n",
" \n",
" reflb1 = input_xarray.sel(wavelengths=band1, method='nearest')\n",
Expand All @@ -194,10 +221,8 @@
{
"cell_type": "code",
"execution_count": null,
"id": "e65d363f-03ce-4efc-b826-aebc3a21d8ab",
"metadata": {
"scrolled": true
},
"id": "2ec41d17-14de-4af5-9c49-d3c19f0bafd9",
"metadata": {},
"outputs": [],
"source": [
"ndvi2.hvplot.image(cmap='viridis', geo=True, tiles='ESRI', aspect = 'equal', frame_width=720, clim=(0,1)).opts(title=\"NDVI Image\")"
Expand Down Expand Up @@ -238,37 +263,66 @@
"source": [
"### Calculate the Normalized-Difference Water Index (NDWI)\n",
"\n",
"https://en.wikipedia.org/wiki/Normalized_difference_water_index"
"We can also calculate a range of other useful indices using the same basic normalized difference vegetation index approach. For example, if we want to investigate the variation in plant / surface water content we can use an SVI such as the Normalized-Difference Water Index [(NDWI)](https://en.wikipedia.org/wiki/Normalized_difference_water_index). Like NDVI, the NDWI uses two different bands to enhance the detection of a plant property, in this case water content, and does that by using a band centered in the NIR and another band centered in the shortwave infrared that is sensitive to plant water content using the following equation: NDWI = (NIR – SWIR)/(NIR + SWIR).\n",
"\n",
"Because it has the same basic structure as NDVI we can still use the same **normalized_diff()** function but instead replace the default bands with 2200nm and 864nm"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c7b165d2-8dae-4c9c-bfe1-fca4ef90ca94",
"id": "9aa99cea-623a-4ba9-b16b-c1869a0b8f2f",
"metadata": {},
"outputs": [],
"source": [
"ds_geo['wavelengths'].data"
"ndwi = normalized_diff(input_xarray = ds_geo, band1=2200, band2=864)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9aa99cea-623a-4ba9-b16b-c1869a0b8f2f",
"id": "46e2a566-ec92-4824-abb5-142629fab27d",
"metadata": {},
"outputs": [],
"source": [
"ndwi = normalized_diff(input_xarray = ds_geo, band1=2200, band2=864)"
"ndwi.hvplot.image(cmap='viridis', geo=True, tiles='ESRI', aspect = 'equal', frame_width=720, clim=(0,1)).opts(title=\"NDWI Image\")"
]
},
{
"cell_type": "markdown",
"id": "30f33351-d608-46ac-9da3-4d5cd600c0bf",
"metadata": {},
"source": [
"### Calculate red-edge NDVI\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "46e2a566-ec92-4824-abb5-142629fab27d",
"id": "e1960c24-1d22-4229-b102-fcf9f0955067",
"metadata": {},
"outputs": [],
"source": [
"ndwi.hvplot.image(cmap='viridis', geo=True, tiles='ESRI', aspect = 'equal', frame_width=720, clim=(0,1)).opts(title=\"NDVI Image\")"
"rendvi = normalized_diff(input_xarray = ds_geo, band1=705, band2=750)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "64f6c1e1-006a-493d-91c0-c16d16237aed",
"metadata": {},
"outputs": [],
"source": [
"rendvi.hvplot.image(cmap='viridis', geo=True, tiles='ESRI', aspect = 'equal', frame_width=720, clim=(0,1)).opts(title=\"reNDVI Image\")"
]
},
{
"cell_type": "markdown",
"id": "0ef27072-9f83-4589-aa9f-1b26270cf578",
"metadata": {},
"source": [
"### Calculate the Photochemical Reflectance Index"
]
},
{
Expand All @@ -278,7 +332,7 @@
"metadata": {},
"outputs": [],
"source": [
"pri_image = pri2(input_xarray = ds_geo, band1=531, band2=570)"
"pri_image = pri(input_xarray = ds_geo, band1=531, band2=570, scaled=True)"
]
},
{
Expand All @@ -288,23 +342,43 @@
"metadata": {},
"outputs": [],
"source": [
"pri_image.hvplot.image(cmap='viridis', geo=True, tiles='ESRI', aspect = 'equal', frame_width=720, clim=(0,0.6)).opts(title=\"PRI Image\")"
"pri_image.hvplot.image(cmap='viridis', geo=True, tiles='ESRI', aspect = 'equal', frame_width=720, clim=(0.44,0.48)).opts(title=\"PRI Image\")"
]
},
{
"cell_type": "markdown",
"id": "ba4f24fb-7332-4a4a-92a1-4afeda129fef",
"metadata": {},
"source": [
"### Calculate a simple ratio index\n",
"\n",
"Let's now calculate another common set of indices, called \"simple ratio\" indices. In this case, we will calculate what is known as the \"Water Band Index\" (WBI)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8519cd8e-8539-4b5f-9cb2-231fe59498ea",
"metadata": {},
"outputs": [],
"source": [
"wbi = simple_ratio(input_xarray = ds_geo, band1=900, band2=970)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a71e48a1-cbce-4113-83e1-2e50172faee6",
"id": "60893cdb-d2da-43e3-b58a-4fba6a55bddb",
"metadata": {},
"outputs": [],
"source": [
"pri_image.hvplot.image(cmap='viridis', geo=True, tiles='ESRI', aspect = 'equal', frame_width=720).opts(title=\"PRI Image\")"
"wbi.hvplot.image(cmap='viridis', geo=True, tiles='ESRI', aspect = 'equal', frame_width=720, clim=(0.8,1.2)).opts(title=\"WBI Image\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "38eceb8e-7de7-4a66-ae91-a627b81a9ffd",
"id": "0bd6dc7d-f6c1-4791-94a6-a7338b04a113",
"metadata": {},
"outputs": [],
"source": []
Expand Down
Binary file modified tools/functions/__pycache__/spectral_index.cpython-310.pyc
Binary file not shown.
39 changes: 36 additions & 3 deletions tools/functions/spectral_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def normalized_diff(input_xarray, band1=650, band2=850):
return(ndiff_image)


def pri2(input_xarray, band1=531, band2=570):
def pri(input_xarray, band1=531, band2=570, scaled=True):
"""
This function takes an input orthorectified EMIT xarray image and calculates a Photochemical Reflectance Index (PRI)
image based on the selected bands. This function outputs the PRI2, which is the PRI scaled on a 0-1 scale
Expand All @@ -38,12 +38,45 @@ def pri2(input_xarray, band1=531, band2=570):
param: band1 the EMIT band number to use for band 1
param: band2 the EMIT band number to use for band 2
param: scaled scale the output pri
"""

reflb1 = input_xarray.sel(wavelengths=band1, method='nearest')
reflb2 = input_xarray.sel(wavelengths=band2, method='nearest')
pri = (reflb1-reflb2)/(reflb1+reflb2)
pri2_output = (pri+1)/2
return(pri2_output)

if scaled == True:
pri = (pri+1)/2

return(pri)


def simple_ratio(input_xarray, band1=900, band2=970):
"""
This function takes an input orthorectified EMIT xarray image and calculates a simple ration index image based on
the selected bands. The assumption is the input image is an EMIT image in xarray
format prepared using the emit_xarray() function from emit_tools.
Function parameters:
param: input_xarray is the input EMIT xarray image currently located in memory; at present this will not accept
the full path to a file that hasn't been loaded into memory yet (TODO)
param: band1 the EMIT band number to use for band 1
param: band2 the EMIT band number to use for band 2
"""

reflb1 = input_xarray.sel(wavelengths=band1, method='nearest')
reflb2 = input_xarray.sel(wavelengths=band2, method='nearest')
sr = reflb1/reflb2

return(sr)








0 comments on commit 1199622

Please sign in to comment.