├── 03_gap_filling ├── models │ └── README └── marmande_dataset │ ├── vector_data.zip │ └── LICENSE ├── 02_semantic_segmentation ├── amsterdam_dataset │ └── terrain_truth │ │ ├── amsterdam_labelimage.tif │ │ └── LICENSE └── models │ └── create_model4.py ├── 01_patch_based_classification ├── tokyo_dataset │ └── terrain_truth │ │ ├── terrain_truth_epsg32654_A.tif │ │ ├── terrain_truth_epsg32654_B.tif │ │ ├── LICENSE │ │ └── legend_style.qml └── models │ ├── create_model1.py │ ├── create_model2.py │ └── create_model3.py └── README.md /03_gap_filling/models/README: -------------------------------------------------------------------------------- 1 | Please download the model from https://github.com/remicres/Deep-Gapfill 2 | -------------------------------------------------------------------------------- /03_gap_filling/marmande_dataset/vector_data.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remicres/otbtf_tutorials_resources/HEAD/03_gap_filling/marmande_dataset/vector_data.zip -------------------------------------------------------------------------------- /02_semantic_segmentation/amsterdam_dataset/terrain_truth/amsterdam_labelimage.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remicres/otbtf_tutorials_resources/HEAD/02_semantic_segmentation/amsterdam_dataset/terrain_truth/amsterdam_labelimage.tif -------------------------------------------------------------------------------- /01_patch_based_classification/tokyo_dataset/terrain_truth/terrain_truth_epsg32654_A.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remicres/otbtf_tutorials_resources/HEAD/01_patch_based_classification/tokyo_dataset/terrain_truth/terrain_truth_epsg32654_A.tif -------------------------------------------------------------------------------- /01_patch_based_classification/tokyo_dataset/terrain_truth/terrain_truth_epsg32654_B.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remicres/otbtf_tutorials_resources/HEAD/01_patch_based_classification/tokyo_dataset/terrain_truth/terrain_truth_epsg32654_B.tif -------------------------------------------------------------------------------- /03_gap_filling/marmande_dataset/LICENSE: -------------------------------------------------------------------------------- 1 | [![License: ODbL](https://img.shields.io/badge/License-ODbL-brightgreen.svg)](https://opendatacommons.org/licenses/odbl/) 2 | 3 | Copyright Remi Cresson (INRAE). 4 | Data are available under the Open Database License. 5 | 6 | -------------------------------------------------------------------------------- /02_semantic_segmentation/amsterdam_dataset/terrain_truth/LICENSE: -------------------------------------------------------------------------------- 1 | [![License: ODbL](https://img.shields.io/badge/License-ODbL-brightgreen.svg)](https://opendatacommons.org/licenses/odbl/) 2 | 3 | Copyright OpenStreetMap contributors. 4 | Data are available under the Open Database License. 5 | See openstreetmap.org, opendatacommons.org 6 | -------------------------------------------------------------------------------- /01_patch_based_classification/tokyo_dataset/terrain_truth/LICENSE: -------------------------------------------------------------------------------- 1 | [![License: ODbL](https://img.shields.io/badge/License-ODbL-brightgreen.svg)](https://opendatacommons.org/licenses/odbl/) 2 | 3 | Copyright OpenStreetMap contributors 4 | Copyright K. Ose, R. Cresson (INRAE/UMR TETIS) 5 | 6 | Data are available under the Open Database License. 7 | See openstreetmap.org, opendatacommons.org 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deep learning tutorial with OTB and TensorFlow 2 | 3 | Codes and documents are licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License][cc-by-sa]. 4 | 5 | [![CC BY-SA 4.0][cc-by-sa-shield]][cc-by-sa] 6 | 7 | [cc-by-sa]: http://creativecommons.org/licenses/by-sa/4.0/ 8 | [cc-by-sa-shield]: https://img.shields.io/badge/License-CC%20BY--SA%204.0-lightgrey.svg 9 | 10 | Terrain truth data derive from OpenStreetMap, which is licensed under [ODbL license][odbl]. 11 | 12 | [![ODbL][odbl-shield]][odbl] 13 | 14 | [odbl]: https://opendatacommons.org/licenses/odbl/ 15 | [odbl-shield]: https://img.shields.io/badge/License-ODbL-brightgreen.svg 16 | 17 | -------------------------------------------------------------------------------- /01_patch_based_classification/models/create_model1.py: -------------------------------------------------------------------------------- 1 | from tricks import * 2 | import sys 3 | import os 4 | 5 | nclasses=6 6 | 7 | def myModel(x): 8 | 9 | # input patches: 16x16x4 10 | conv1 = tf.layers.conv2d(inputs=x, filters=16, kernel_size=[5,5], padding="valid", 11 | activation=tf.nn.relu) # out size: 12x12x16 12 | pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2) # out: 6x6x16 13 | conv2 = tf.layers.conv2d(inputs=pool1, filters=16, kernel_size=[3,3], padding="valid", 14 | activation=tf.nn.relu) # out size: 4x4x16 15 | pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2) # out: 2x2x16 16 | conv3 = tf.layers.conv2d(inputs=pool2, filters=32, kernel_size=[2,2], padding="valid", 17 | activation=tf.nn.relu) # out size: 1x1x32 18 | 19 | # Features 20 | features = tf.reshape(conv3, shape=[-1, 32], name="features") 21 | 22 | # Neurons for classes 23 | estimated = tf.layers.dense(inputs=features, units=nclasses, activation=None) 24 | estimated_label = tf.argmax(estimated, 1, name="prediction") 25 | 26 | return estimated, estimated_label 27 | 28 | """ Main """ 29 | if len(sys.argv) != 2: 30 | print("Usage : ") 31 | sys.exit(1) 32 | 33 | # Create the TensorFlow graph 34 | with tf.Graph().as_default(): 35 | 36 | # Placeholders 37 | x = tf.placeholder(tf.float32, [None, None, None, 4], name="x") 38 | y = tf.placeholder(tf.int32 , [None, None, None, 1], name="y") 39 | lr = tf.placeholder_with_default(tf.constant(0.0002, dtype=tf.float32, shape=[]), 40 | shape=[], name="lr") 41 | 42 | # Output 43 | y_estimated, y_label = myModel(x) 44 | 45 | # Loss function 46 | cost = tf.losses.sparse_softmax_cross_entropy(labels=tf.reshape(y, [-1, 1]), 47 | logits=tf.reshape(y_estimated, [-1, nclasses])) 48 | 49 | # Optimizer 50 | optimizer = tf.train.AdamOptimizer(learning_rate=lr, name="optimizer").minimize(cost) 51 | 52 | # Initializer, saver, session 53 | init = tf.global_variables_initializer() 54 | saver = tf.train.Saver( max_to_keep=20 ) 55 | sess = tf.Session() 56 | sess.run(init) 57 | 58 | # Create a SavedModel 59 | create_savedmodel(sess, ["x:0", "y:0"], ["features:0", "prediction:0"], sys.argv[1]) 60 | -------------------------------------------------------------------------------- /01_patch_based_classification/tokyo_dataset/terrain_truth/legend_style.qml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 1 6 | 1 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | MinMax 19 | WholeRaster 20 | Estimated 21 | 0.02 22 | 0.98 23 | 2 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 0 48 | 49 | -------------------------------------------------------------------------------- /01_patch_based_classification/models/create_model2.py: -------------------------------------------------------------------------------- 1 | from tricks import * 2 | import sys 3 | import os 4 | 5 | nclasses=6 6 | 7 | def myModel(x): 8 | 9 | # input patches: 16x16x4 10 | conv1 = tf.layers.conv2d(inputs=x, filters=16, kernel_size=[5,5], padding="valid", 11 | activation=tf.nn.relu) # out size: 12x12x16 12 | conv2 = tf.layers.conv2d(inputs=conv1, filters=16, kernel_size=[3,3], padding="valid", 13 | activation=tf.nn.relu) # out size: 10x10x16 14 | conv3 = tf.layers.conv2d(inputs=conv2, filters=16, kernel_size=[3,3], padding="valid", 15 | activation=tf.nn.relu) # out size: 8x8x16 16 | conv4 = tf.layers.conv2d(inputs=conv3, filters=32, kernel_size=[3,3], padding="valid", 17 | activation=tf.nn.relu) # out size: 6x6x32 18 | conv5 = tf.layers.conv2d(inputs=conv4, filters=32, kernel_size=[3,3], padding="valid", 19 | activation=tf.nn.relu) # out size: 4x4x32 20 | conv6 = tf.layers.conv2d(inputs=conv5, filters=32, kernel_size=[3,3], padding="valid", 21 | activation=tf.nn.relu) # out size: 2x2x32 22 | conv7 = tf.layers.conv2d(inputs=conv6, filters=32, kernel_size=[2,2], padding="valid", 23 | activation=tf.nn.relu) # out size: 1x1x32 24 | 25 | # Features 26 | features = tf.reshape(conv7, shape=[-1, 32], name="features") 27 | 28 | # Neurons for classes 29 | estimated = tf.layers.dense(inputs=features, units=nclasses, activation=None) 30 | estimated_label = tf.argmax(estimated, 1, name="prediction") 31 | 32 | return estimated, estimated_label 33 | 34 | """ Main """ 35 | if len(sys.argv) != 2: 36 | print("Usage : ") 37 | sys.exit(1) 38 | 39 | # Create the TensorFlow graph 40 | with tf.Graph().as_default(): 41 | 42 | # Placeholders 43 | x = tf.placeholder(tf.float32, [None, None, None, 4], name="x") 44 | y = tf.placeholder(tf.int32 , [None, None, None, 1], name="y") 45 | lr = tf.placeholder_with_default(tf.constant(0.0002, dtype=tf.float32, shape=[]), 46 | shape=[], name="lr") 47 | 48 | # Output 49 | y_estimated, y_label = myModel(x) 50 | 51 | # Loss function 52 | cost = tf.losses.sparse_softmax_cross_entropy(labels=tf.reshape(y, [-1, 1]), 53 | logits=tf.reshape(y_estimated, [-1, nclasses])) 54 | 55 | # Optimizer 56 | optimizer = tf.train.AdamOptimizer(learning_rate=lr, name="optimizer").minimize(cost) 57 | 58 | # Initializer, saver, session 59 | init = tf.global_variables_initializer() 60 | saver = tf.train.Saver( max_to_keep=20 ) 61 | sess = tf.Session() 62 | sess.run(init) 63 | 64 | # Create a SavedModel 65 | create_savedmodel(sess, ["x:0", "y:0"], ["features:0", "prediction:0"], sys.argv[1]) 66 | -------------------------------------------------------------------------------- /02_semantic_segmentation/models/create_model4.py: -------------------------------------------------------------------------------- 1 | from tricks import * 2 | import sys 3 | import os 4 | 5 | nclasses=2 6 | 7 | # Convolution block with strides 2 ("downsampling") 8 | def _conv(inp, n, k_size=3, strides=2, activ=tf.nn.relu): 9 | out = tf.layers.conv2d( 10 | inputs=inp, 11 | filters=n, 12 | kernel_size=[k_size, k_size], 13 | padding="same", 14 | strides=(strides, strides), 15 | activation=activ) 16 | return out 17 | 18 | # Transposed convolution block with strides 2 ("upsampling") 19 | def _dconv(inp, n, k_size=3, strides=2, activ=tf.nn.relu): 20 | out = tf.layers.conv2d_transpose( 21 | inputs=inp, 22 | filters=n, 23 | strides=(strides,strides), 24 | kernel_size=[k_size, k_size], 25 | padding="same", 26 | activation=activ) 27 | return out 28 | 29 | def myModel(x): 30 | 31 | depth = 16 32 | 33 | # Encoding 34 | conv1 = _conv(x, 1*depth) # 64 x 64 --> 32 x 32 (31 x 31) 35 | conv2 = _conv(conv1, 2*depth) # 32 x 32 --> 16 x 16 (15 x 15) 36 | conv3 = _conv(conv2, 4*depth) # 16 x 16 --> 8 x 8 ( 7 x 7) 37 | conv4 = _conv(conv3, 4*depth) # 8 x 8 --> 4 x 4 ( 3 x 3) 38 | 39 | # Decoding (with skip connections) 40 | deconv1 = _dconv(conv4, 4*depth) # 4 x 4 --> 8 x 8 ( 5 x 5) 41 | deconv2 = _dconv(deconv1 + conv3, 2*depth) # 8 x 8 --> 16 x 16 ( 9 x 9) 42 | deconv3 = _dconv(deconv2 + conv2, 1*depth) # 16 x 16 --> 32 x 32 (17 x 17) 43 | deconv4 = _dconv(deconv3 + conv1, 1*depth) # 32 x 32 --> 64 x 64 (33 x 33) 44 | 45 | # Neurons for classes 46 | estimated = tf.layers.dense(inputs=deconv4, units=nclasses, activation=None) 47 | 48 | return estimated 49 | 50 | """ Main """ 51 | # check number of arguments 52 | if len(sys.argv) != 2: 53 | print("Usage : ") 54 | sys.exit(1) 55 | 56 | # Create the graph 57 | with tf.Graph().as_default(): 58 | 59 | # Placeholders 60 | x = tf.placeholder(tf.float32, [None, None, None, 4], name="x") 61 | y = tf.placeholder(tf.int32 , [None, None, None, 1], name="y") 62 | lr = tf.placeholder_with_default(tf.constant(0.0002, dtype=tf.float32, shape=[]), 63 | shape=[], name="lr") 64 | 65 | # Output neurons of the model 66 | y_estimated = myModel(x) 67 | 68 | # Prediction output 69 | y_class = tf.argmax(y_estimated, axis=3) 70 | 71 | # Add the 4th dimension to have (#, :, :, 1) so the OTBTF apps 72 | # know that the output image has 1 component 73 | y_pred = tf.expand_dims(y_class, axis=-1, name="prediction") 74 | 75 | # Prediction output (FCN) 76 | y_pred_fcn = tf.identity(y_pred[:, 16:-16, 16:-16, :], name="prediction_fcn") 77 | 78 | # Loss function 79 | cost = tf.losses.sparse_softmax_cross_entropy(labels=tf.reshape(y, [-1, 1]), 80 | logits=tf.reshape(y_estimated, [-1, nclasses])) 81 | 82 | # Optimizer 83 | optimizer = tf.train.AdamOptimizer(learning_rate=lr, name="optimizer").minimize(cost) 84 | 85 | # Initializer, saver, session 86 | init = tf.global_variables_initializer() 87 | saver = tf.train.Saver( max_to_keep=20 ) 88 | sess = tf.Session() 89 | sess.run(init) 90 | 91 | # Create a SavedModel 92 | create_savedmodel(sess, ["x:0","y:0"], ["prediction:0", "prediction_fcn:0"], sys.argv[1]) 93 | -------------------------------------------------------------------------------- /01_patch_based_classification/models/create_model3.py: -------------------------------------------------------------------------------- 1 | from tricks import * 2 | import sys 3 | import os 4 | 5 | nclasses=6 6 | 7 | def myModel(x1,x2): 8 | 9 | # The 20m spacing branch (input patches: 8x8x3) 10 | conv1_x1 = tf.layers.conv2d(inputs=x1, filters=16, kernel_size=[3,3], padding="valid", 11 | activation=tf.nn.relu) # out size: 6x6x16 12 | conv2_x1 = tf.layers.conv2d(inputs=conv1_x1, filters=32, kernel_size=[3,3], padding="valid", 13 | activation=tf.nn.relu) # out size: 4x4x32 14 | conv3_x1 = tf.layers.conv2d(inputs=conv2_x1, filters=64, kernel_size=[3,3], padding="valid", 15 | activation=tf.nn.relu) # out size: 2x2x64 16 | conv4_x1 = tf.layers.conv2d(inputs=conv3_x1, filters=64, kernel_size=[2,2], padding="valid", 17 | activation=tf.nn.relu) # out size: 1x1x64 18 | 19 | # The 10m spacing branch (input patches: 16x16x4) 20 | conv1_x2 = tf.layers.conv2d(inputs=x2, filters=16, kernel_size=[5,5], padding="valid", 21 | activation=tf.nn.relu) # out size: 12x12x16 22 | conv2_x2 = tf.layers.conv2d(inputs=conv1_x2, filters=32, kernel_size=[3,3], padding="valid", 23 | activation=tf.nn.relu) # out size: 10x10x32 24 | conv3_x2 = tf.layers.conv2d(inputs=conv2_x2, filters=64, kernel_size=[3,3], padding="valid", 25 | activation=tf.nn.relu) # out size: 8x8x64 26 | conv4_x2 = tf.layers.conv2d(inputs=conv3_x2, filters=64, kernel_size=[3,3], padding="valid", 27 | activation=tf.nn.relu) # out size: 6x6x64 28 | conv5_x2 = tf.layers.conv2d(inputs=conv4_x2, filters=64, kernel_size=[3,3], padding="valid", 29 | activation=tf.nn.relu) # out size: 4x4x64 30 | pool1_x2 = tf.layers.max_pooling2d(inputs=conv5_x2, pool_size=[2, 2], 31 | strides=2) # out size: 2x2x64 32 | conv6_x2 = tf.layers.conv2d(inputs=pool1_x2, filters=64, kernel_size=[2,2], padding="valid", 33 | activation=tf.nn.relu) # out size: 1x1x64 34 | 35 | # Stack features from the two branches 36 | features = tf.reshape(tf.stack([conv4_x1, conv6_x2], axis=3), 37 | shape=[-1, 128], name="features") 38 | 39 | # Fully connected layer 40 | dense_1 = tf.layers.dense(inputs=features, units=128, activation=tf.nn.relu) 41 | 42 | # Neurons for classes 43 | estimated = tf.layers.dense(inputs=dense_1, units=nclasses, activation=None) 44 | estimated_label = tf.argmax(estimated, 1, name="prediction") 45 | 46 | return estimated, estimated_label 47 | 48 | """ Main """ 49 | # check number of arguments 50 | if len(sys.argv) != 2: 51 | print("Usage : ") 52 | sys.exit(1) 53 | 54 | # Create the graph 55 | with tf.Graph().as_default(): 56 | 57 | # Placeholders 58 | x1 = tf.placeholder(tf.float32, [None, None, None, 6], name="x1") 59 | x2 = tf.placeholder(tf.float32, [None, None, None, 4], name="x2") 60 | y = tf.placeholder(tf.int32 , [None, None, None, 1], name="y") 61 | lr = tf.placeholder_with_default(tf.constant(0.0002, dtype=tf.float32, shape=[]), 62 | shape=[], name="lr") 63 | 64 | # Output 65 | y_estimated, y_label = myModel(x1,x2) 66 | 67 | # Loss function 68 | cost = tf.losses.sparse_softmax_cross_entropy(labels=tf.reshape(y, [-1, 1]), 69 | logits=tf.reshape(y_estimated, [-1, nclasses])) 70 | 71 | # Optimizer 72 | optimizer = tf.train.AdamOptimizer(learning_rate=lr, name="optimizer").minimize(cost) 73 | 74 | # Initializer, saver, session 75 | init = tf.global_variables_initializer() 76 | saver = tf.train.Saver( max_to_keep=20 ) 77 | sess = tf.Session() 78 | sess.run(init) 79 | 80 | # Create a SavedModel 81 | create_savedmodel(sess, ["x1:0", "x2:0", "y:0"], ["features:0", "prediction:0"], sys.argv[1]) 82 | --------------------------------------------------------------------------------