Streams extraction using deep learning

  • 🔬 Data Science
  • 🥠 Deep Learning and Pixel Classification

Introduction

In this notebook, we will use 3 different geo-morphological characteristics derived from a 5 m resolution DEM in one of the watersheds of Alaska to extract streams. These 3 characteristics are Topographic position index derived from 3 cells, Geomorphon landform, and topographic wetness index.

We created a composite out of these 3 characteristic rasters and used Export raster tool to convert scale the pixels values to 8 bit unsigned. Subsequently, the images are exported as "Classified Tiles" to train a Multi-Task Road Extractor model provided by ArcGIS API for Python for extracting the streams.

Before proceeding through this notebook, it is advised to go through the API Reference for Multi-Task Road Extractor. It will help in understanding the Multi-Task Road Extractor's workflow in detail.

Necessary imports

import os
import zipfile
from pathlib import Path

from arcgis.gis import GIS
from arcgis.learn import prepare_data, MultiTaskRoadExtractor, ConnectNet

Connect to your GIS

gis = GIS("home")
ent_gis = GIS('https://pythonapi.playground.esri.com/portal', 'arcgis_python', 'amazing_arcgis_123')

Get the data for analysis

Here is the composite with 3 bands representing the 3 geo-morphological characteristics namely Topographic position index, Geomorphon landform, and Topographic wetness index.

composite_raster = ent_gis.content.get('43c4824dd4bf41ee886be5042262f192')
composite_raster
composite_3bands_6BC_8BC_8bitunsigned
Image by api_data_owner
Last Modified: March 04, 2021
0 comments, 118 views
BeaverCreek_Flowlines = ent_gis.content.get('2b198075e53748b4ab84197bc6ddd478')
BeaverCreek_Flowlines
BeaverCreek_Flowlines
Feature Layer Collection by api_data_owner
Last Modified: March 04, 2021
0 comments, 92 views

Export training data

Export training data using 'Export Training data for deep learning' tool, click here for detailed documentation:

  • Set 'composite_3bands_6BC_8BC_8bitunsigned' as Input Raster.
  • Set a location where you want to export the training data in Output Folder parameter, it can be an existing folder or the tool will create that for you.
  • Set the 'BeaverCreek_Flowlines' as input to the Input Feature Class Or Classified Raster parameter.
  • Set Class Field Value as 'FClass'.
  • Set Image Format as 'TIFF format'
  • Tile Size X & Tile Size Y can be set to 256.
  • Stride X & Stride Y can be set to 128.
  • Select 'Classified Tiles' as the Meta Data Format.
  • In 'Environments' tab set an optimum Cell Size. For this example, as we have performing the analysis on the geo-morphological characteristics with 5 m resolution, so, we used '5' as the cell size.

arcpy.ia.ExportTrainingDataForDeepLearning("composite_3bands_6BC_8BC_8bitunsigned.tif", r"D:\Stream Extraction\Exported_3bands_composite_8bit_unsigned", "BeaverCreek_Flowlines", "TIFF", 256, 256, 128, 128, "ONLY_TILES_WITH_FEATURES", "Classified_Tiles", 0, "FClass", 5, None, 0, "MAP_SPACE", "PROCESS_AS_MOSAICKED_IMAGE", "NO_BLACKEN", "FIXED_SIZE")

Alternatively, we have provided a subset of training data containing a few samples. You can use the data directly to run the experiments.

training_data = gis.content.get('3a95fd7a25d54898bddabf1989eea87d')
training_data
streams_extraction_using_connectnet
Image Collection by api_data_owner
Last Modified: March 08, 2021
0 comments, 22 views
filepath = training_data.download(file_name=training_data.name)
with zipfile.ZipFile(filepath, 'r') as zip_ref:
    zip_ref.extractall(Path(filepath).parent)
output_path = Path(os.path.join(os.path.splitext(filepath)[0]))

Prepare data

We will specify the path to our training data and a few hyperparameters.

  • path: path of the folder containing training data.
  • batch_size: Number of images your model will train on each step inside an epoch, it directly depends on the memory of your graphic card. 8 worked for us on a 11GB GPU.
data = prepare_data(output_path, chip_size=512, batch_size=4)

Visualize a few samples from your training data

To get a sense of what the training data looks like, arcgis.learn.show_batch() method randomly picks a few training chips and visualizes them.

  • rows: number of rows we want to see the results for.
data.show_batch()
<Figure size 1152x1152 with 16 Axes>

Train the model

Load model architecture

arcgis.learn provides the MultiTaskRoadExtractor model for classifying linear features, which is based on multi-task learning mechanism. More details about multi-task learning can be found here.

model = ConnectNet(data, mtl_model="hourglass")

Find an optimal learning rate

Learning rate is one of the most important hyperparameters in model training. Here, we explore a range of learning rate to guide us to choose the best one. We will use the lr_find() method to find an optimum learning rate at which we can train a robust model.

lr=model.lr_find()
<Figure size 432x288 with 1 Axes>

Fit the model

To train the model, we use the fit() method. To start, we will train our model for 50 epochs. Epoch defines how many times model is exposed to entire training set. We have passes three parameters to fit() method:

  • epochs: Number of cycles of training on the data.
  • lr: Learning rate to be used for training the model.
  • wd: Weight decay to be used.
model.fit(50, lr)
epochtrain_lossvalid_lossaccuracymioudicetime
00.5587660.5347090.9220760.7520230.69197706:12
10.4987510.4924640.9240210.7537330.68888706:14
20.4379320.4368720.9276990.7742620.72860906:15
30.4267380.4176980.9290650.7807040.74242506:16
40.3979520.4022510.9329540.7916820.75638506:22
50.3846750.4171340.9295470.7699490.71340706:16
60.3439370.3968170.9361830.7998150.76260606:14
70.3030770.3609510.9438920.8238770.79733106:13
80.2736350.3463680.9456770.8326080.81631606:12
90.2369090.3584290.9474440.8347440.81456406:11
100.2103000.2943130.9606340.8711120.85303806:11
110.1961870.2763270.9642960.8813240.86191406:10
120.1703320.2628380.9680500.8907940.86684406:17
130.1540120.2317410.9736620.9102230.89240106:08
140.1456260.2811050.9650280.8797890.85457006:09
150.1283960.2033110.9779360.9237660.91151906:10
160.1209950.1972920.9794870.9284560.91299606:10
170.1139160.1999370.9793690.9283370.91226906:10
180.1045170.1712650.9836060.9431750.93769306:09
190.0905870.1704760.9856220.9496360.94114006:10
200.0871830.1839740.9827690.9397930.93011806:10
210.0851350.1649840.9860160.9510640.94298006:10
220.0764130.1555050.9868020.9541210.94989606:09
230.0693360.1681830.9845520.9452560.93860706:09
240.0670830.2045120.9800710.9318270.92194906:10
250.0677900.1815790.9835720.9430360.93199106:08
260.0579530.1443680.9901690.9653320.96394206:11
270.0581470.1446590.9878780.9571730.94465306:10
280.0498240.1330090.9914820.9699180.96875506:09
290.0447230.1411820.9912260.9690110.96245506:10
300.0422760.1302060.9921010.9716940.96672006:09
310.0392410.1376590.9922480.9724940.96759006:10
320.0370550.1473080.9925270.9734410.97145606:11
330.0343510.1322640.9929710.9753720.97469106:07
340.0305990.1377690.9933460.9765760.97587206:10
350.0295660.1419840.9939010.9784610.97439006:09
360.0264600.1427560.9940960.9790010.97465306:10
370.0224540.1503790.9945560.9805060.97676906:10
380.0204500.1542170.9948580.9816570.97775106:09
390.0186120.1602420.9949380.9819970.97988206:10
400.0168240.1687300.9948490.9817170.97827206:09
410.0157020.1785810.9951220.9826920.98035506:13
420.0140450.1929320.9950070.9823010.97987206:11
430.0126250.2004340.9953300.9834680.98175206:08
440.0111610.2101730.9955460.9842420.98251306:09
450.0101150.2206120.9956000.9844340.98256506:09
460.0093990.2305110.9956310.9845420.98255406:10
470.0089470.2368330.9956500.9846080.98262006:08
480.0087200.2393710.9956740.9846850.98275706:09
490.0086180.2392660.9956960.9847610.98280106:09

As you can see, both the losses (valid_loss and train_loss) started from a higher value and ended up to a lower value, that tells our model has learnt well. Let us do an accuracy assessment to validate our observation.

Accuracy Assessment

We can compute the mIOU (Mean Intersection Over Union) for the model we just trained in order to do the accuracy assessment. We can compute the mIOU by calling model.mIOU. It takes the following parameters:

  • mean: If False returns class-wise mean IOU, otherwise returns mean IOU of all classes combined.
model.mIOU()
100.00% [62/62 00:35<00:00]
{'0': 0.9947645938447383, 1: 0.9746375491545367}

The model has a mean IOU of 0.97 which proves that the model has learnt well. Let us now see it's results on validation set.

Visualize results in validation set

The code below will pick a few random samples and show us ground truth and respective model predictions side by side. This allows us to validate the results of your model in the notebook itself. Once satisfied we can save the model and use it further in our workflow. The model.show_results() method can be used to display the detected streams.

model.show_results(rows=4)
<Figure size 576x1152 with 8 Axes>

Save the model

We would now save the model which we just trained as a 'Deep Learning Package' or '.dlpk' format. Deep Learning package is the standard format used to deploy deep learning models on the ArcGIS platform.

We will use the save() method to save the model and by default it will be saved to a folder 'models' inside our training data folder itself.

model.save('stream_ext_50e', overwrite=True, publish=True, gis=gis)
Published DLPK Item Id: 2a8b0ca302ca42d9a979b5e28bebc6d7
WindowsPath('D:/data/stream_ext_50e')

Model inference

The saved model can be used to classify streams using the Classify Pixels Using Deep Learning tool available in ArcGIS Pro, or ArcGIS Enterprise.

arcpy.ia.ClassifyPixelsUsingDeepLearning("composite_3bands_6BC_8BC_8bitunsigned.tif", r"D:/stream_ext_20e/stream_ext_20e.dlpk", "padding 64;batch_size 4;predict_background True;return_probability_raster False;threshold 0.5", "PROCESS_AS_MOSAICKED_IMAGE", None); out_classified_raster.save(r"\Documents\ArcGIS\Packages\Stream Extraction demo_9ee52d\p20\stream_identification.gdb\detected_streams")

The output of the model is a layer of detected streams which is shown below:

extracted_streams = ent_gis.content.get('389772c5dbb745b79953dc7ee0dc5876')
extracted_streams
detected_streams_connectnet_50e
detected_streams_connectnet_50eImagery Layer by api_data_owner
Last Modified: August 11, 2022
0 comments, 0 views
map1 = gis.map()
map1.add_layer(extracted_streams)
map1

image.png

map1.zoom_to_layer(extracted_streams)

Conclusion

The notebook presents the workflow showing how easily you can combine traditional morphological landform characteristics with deep learning methodologies using arcgis.learn to detect streams.

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.