Choosing the right NIR and NDVI thresholds

The selection of the correct NIR and NDVI thresholds is a crucial step in the river segmentation process. This process is curcial since it will improve the accuracy of the segmentation. Also, this threshold calibration will reduce significantly the amount of manual corrections required after the segmentation is produced.

The SERIF toolbox provides the options of input individual thresholds for each image. These thresholds must stored in the form of CSV files whitin the same folder as the image. The threshols definition and the further obtention of the CSV files can be done usint the following script:

import os
import sys
import numpy as np
import rasterio as rio
sys.path.append(r"path/to/your/SERIF/toolbox")
from src.visualisation import Visualisation

# Initial thresholds to start the calibration
NIR_THRESHOLD = 77
NDVI_THRESHOLD = 0.2

def list_tifs(path: str):
    files_list = []
    for file in os.listdir(path):
        if file.endswith(".tif"):
            files_list.append(file)
    return files_list

def export_file(name: str, thereshold: tuple):
    # Extract file from tif image
    split_path = os.path.split(name)
    file_name = split_path[1].replace(".tif", ".csv")
    file_output_path = os.path.join(split_path[0], file_name)
    data = {
        "image_name": file_name.replace(".csv", ""),
        "ndvi": thereshold[0],
        "nir": thereshold[1]
    }
    # [file_name.replace(".csv",""),thereshold[0],thereshold[1]]
    # col_names = ["image_name","ndvi","nir"]
    df = pd.DataFrame(data=data, index=[0])
    df = df.set_index("image_name")
    # if not os.path.exists(file_output_path):
    df.to_csv(file_output_path, sep=",")

def main():
    # Set the paths to the photos
    photos_path = r"path/to/your/photos"
    center_line_path = r"path/to/your/centerline/shapefile.shp" #This shapefile must be in the same CRS as the photos
    files_list = list_tifs(photos_path)
    visualisation = Visualisation(center_line_path)
    for file in files_list:
        img_path = os.path.join(photos_path, file)
        with rio.open(img_path) as src:
            visualisation.dataset_rio = src
            extent_clip = visualisation.intersect_tif_and_shp()
            # Get bands
            red :np.ndarray = src.read(1)
            nir :np.ndarray = src.read(4)
            # Clip bands
            red = red[extent_clip.loc[0, "Index"]:extent_clip.loc[2, "Index"],
                    extent_clip.loc[1, "Index"]:extent_clip.loc[3, "Index"]]
            nir = nir[extent_clip.loc[0, "Index"]:extent_clip.loc[2, "Index"],
                    extent_clip.loc[1, "Index"]:extent_clip.loc[3, "Index"]]
            ndvi = (nir.astype(float)-red.astype(float)) / \
                (nir.astype(float)+red.astype(float))
            ndvi[np.isnan(ndvi)] = np.nan
            rgb = visualisation.get_RGB(src, extent_clip)
            mask_nir = np.where(nir < NIR_THRESHOLD, 1, 0)
            mask_ndvi = np.where(ndvi < NDVI_THRESHOLD, 1, 0)
            # Refine NIR mask
            mask_nir = np.where((mask_ndvi == 1) &
                                (mask_nir == 1), 1, 0)
            # Get total mask
            mask_ = mask_nir + mask_ndvi
            mask_ = mask_.astype(float)
            mask_[mask_ == 0] = np.nan
            ndvi_threshold, nir_threshold = visualisation.calibrate_threshold(rgb, mask_,
                                                                            ndvi, nir,
                                                                            NIR_THRESHOLD,
                                                                            NDVI_THRESHOLD)
            export_file(img_path, (ndvi_threshold, nir_threshold))

Once the script is run, you will see that a figure will pop in your screen like the following:

Calibration of the NIR and NDVI thresholds

In this figure, the red line represents the NIR threshold and the blue line represents the NDVI threshold. You can interactively move the sliders to find the best thresholds for the current photo. You can zoom in and out to check in more detail a particular area of interest.

If youre are satisfied with the selected thresholds, you just need to close the figure and the CSV file with the selected thresholds will be saved in the same folder as the image. The CSV strcuture should look like the following:

image_name,ndvi,nir
q16039_162_30cm_f05,0.2472023192585575,100