├── README.md ├── convert_label_tool.py ├── crop_tool.py ├── data_samples ├── architecture.png ├── bw_label.png ├── data_sample.jpg ├── input.jpg ├── labelled.png └── unet.png ├── generate_data.py ├── model_e_resunet.py ├── model_resunet.py ├── model_unet.py ├── resize_tool.py ├── rgb2gray.py ├── test.png └── tmp_task.py /README.md: -------------------------------------------------------------------------------- 1 | # Building Extraction with Enhanced ResUnet 2 | Urban building extraction in Daejeon region using Modified Residual U-Net (Modified ResUnet) and applying post-processing. 3 | 4 | Data Sample: 5 | 6 | ![picture](data_samples/data_sample.jpg) 7 | 8 | ## Unet: 9 | 10 | ![picture](data_samples/unet.png) 11 | 12 | ## Enhanced ResUnet: 13 | 14 | ![picture](data_samples/architecture.png) 15 | 16 | Sept. 2019 17 | 18 | Tran Le Anh 19 | -------------------------------------------------------------------------------- /convert_label_tool.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | import glob 4 | import os 5 | 6 | def nothing(*arg): 7 | pass 8 | 9 | if __name__ == '__main__': 10 | 11 | icol = (28, 0, 0, 56, 255, 255) # Green 12 | 13 | cv2.namedWindow('colorTest') 14 | # Lower range colour sliders. 15 | cv2.createTrackbar('lowHue', 'colorTest', icol[0], 255, nothing) 16 | cv2.createTrackbar('lowSat', 'colorTest', icol[1], 255, nothing) 17 | cv2.createTrackbar('lowVal', 'colorTest', icol[2], 255, nothing) 18 | # Higher range colour sliders. 19 | cv2.createTrackbar('highHue', 'colorTest', icol[3], 255, nothing) 20 | cv2.createTrackbar('highSat', 'colorTest', icol[4], 255, nothing) 21 | cv2.createTrackbar('highVal', 'colorTest', icol[5], 255, nothing) 22 | 23 | # img_name = "test.png" 24 | 25 | srcImageName = glob.glob("./labelled_input/*.png") 26 | 27 | for imgName in srcImageName: 28 | 29 | lowHue = cv2.getTrackbarPos('lowHue', 'colorTest') 30 | lowSat = cv2.getTrackbarPos('lowSat', 'colorTest') 31 | lowVal = cv2.getTrackbarPos('lowVal', 'colorTest') 32 | highHue = cv2.getTrackbarPos('highHue', 'colorTest') 33 | highSat = cv2.getTrackbarPos('highSat', 'colorTest') 34 | highVal = cv2.getTrackbarPos('highVal', 'colorTest') 35 | 36 | frame = cv2.imread(imgName) 37 | name = os.path.basename(imgName) 38 | print(name) 39 | 40 | frameHSV = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) 41 | colorLow = np.array([lowHue, lowSat, lowVal]) 42 | colorHigh = np.array([highHue, highSat, highVal]) 43 | mask = cv2.inRange(frameHSV, colorLow, colorHigh) 44 | 45 | 46 | 47 | # Show the mask and the image 48 | cv2.imshow('Threshoding', mask) 49 | cv2.imshow('Input', frame) 50 | 51 | # Write label 52 | cv2.imwrite("./bw_label/" + name, mask) 53 | 54 | k = cv2.waitKey(5) & 0XFF 55 | if k == 27: 56 | break 57 | 58 | cv2.destroyAllWindows() -------------------------------------------------------------------------------- /crop_tool.py: -------------------------------------------------------------------------------- 1 | from matplotlib import pyplot as plt 2 | import cv2 3 | import time 4 | 5 | img = cv2.imread('po_7341_pan_0000000.jpg') 6 | 7 | numCols = 7 8 | numRows = 7 9 | 10 | pieces = [] 11 | 12 | for i in range(numRows): 13 | for j in range(numCols): 14 | from_x = j*img.shape[0]//numCols 15 | from_y = i*img.shape[1]//numRows 16 | to_x = (j+1)*img.shape[0]//numCols 17 | to_y = (i+1)*img.shape[1]//numRows 18 | cropped = img[from_y:to_y, from_x:to_x] 19 | 20 | img_name = str(i)+str(j)+'_7341_7x7.jpg' 21 | 22 | cv2.imwrite(img_name, cropped) 23 | 24 | print(img_name) 25 | 26 | # pieces.append(cropped) 27 | 28 | # fig = plt.figure(figsize=(12, 10)) 29 | # for i in range(len(pieces)): 30 | # fig.add_subplot(numCols, numRows, i+1) 31 | # plt.imshow(pieces[i]) 32 | 33 | # plt.show() -------------------------------------------------------------------------------- /data_samples/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tranleanh/modified-resunet/e170ba75fe4a66757831b4b24b4f0712ab41ce97/data_samples/architecture.png -------------------------------------------------------------------------------- /data_samples/bw_label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tranleanh/modified-resunet/e170ba75fe4a66757831b4b24b4f0712ab41ce97/data_samples/bw_label.png -------------------------------------------------------------------------------- /data_samples/data_sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tranleanh/modified-resunet/e170ba75fe4a66757831b4b24b4f0712ab41ce97/data_samples/data_sample.jpg -------------------------------------------------------------------------------- /data_samples/input.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tranleanh/modified-resunet/e170ba75fe4a66757831b4b24b4f0712ab41ce97/data_samples/input.jpg -------------------------------------------------------------------------------- /data_samples/labelled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tranleanh/modified-resunet/e170ba75fe4a66757831b4b24b4f0712ab41ce97/data_samples/labelled.png -------------------------------------------------------------------------------- /data_samples/unet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tranleanh/modified-resunet/e170ba75fe4a66757831b4b24b4f0712ab41ce97/data_samples/unet.png -------------------------------------------------------------------------------- /generate_data.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import glob 3 | import os 4 | 5 | srcImgName = glob.glob("./input/*.jpg") 6 | trainImgName = glob.glob("./bw_label/*.png") 7 | 8 | for i in range(len(srcImgName)): 9 | 10 | _srcImg = cv2.imread(srcImgName[i]) 11 | name_srcImg = os.path.basename(srcImgName[i]) 12 | name_trainImg = os.path.basename(trainImgName[i]) 13 | 14 | flipVertical = cv2.flip(_srcImg, 0) 15 | flipHorizontal = cv2.flip(_srcImg, 1) 16 | flipBoth = cv2.flip(_srcImg, -1) 17 | 18 | _trainImg = cv2.imread(trainImgName[i]) 19 | # name_trainImg = os.path.basename(imgName) 20 | 21 | flipVertical_mask = cv2.flip(_trainImg, 0) 22 | flipHorizontal_mask = cv2.flip(_trainImg, 1) 23 | flipBoth_mask = cv2.flip(_trainImg, -1) 24 | 25 | name_gen_0 = "0_" + name_srcImg 26 | name_gen_1 = "1_" + name_srcImg 27 | name_gen_2 = "2_" + name_srcImg 28 | 29 | label_name_gen_0 = "0_" + name_trainImg 30 | label_name_gen_1 = "1_" + name_trainImg 31 | label_name_gen_2 = "2_" + name_trainImg 32 | 33 | cv2.imwrite(name_gen_0, flipVertical) 34 | cv2.imwrite(name_gen_1, flipHorizontal) 35 | cv2.imwrite(name_gen_2, flipBoth) 36 | 37 | cv2.imwrite(label_name_gen_0, flipVertical_mask) 38 | cv2.imwrite(label_name_gen_1, flipHorizontal_mask) 39 | cv2.imwrite(label_name_gen_2, flipBoth_mask) -------------------------------------------------------------------------------- /model_e_resunet.py: -------------------------------------------------------------------------------- 1 | # Sept. 2019 2 | # Tran Le Anh, MSc Student 3 | # Satellite Image Processing Lab, Myongji Univ., Yongin, South Korea 4 | # tranleanh.nt@gmail.com 5 | # https://sites.google.com/view/leanhtran 6 | 7 | # Expanded Residual Unet Model (Architecture) 8 | 9 | import numpy as np 10 | import os 11 | import skimage.io as io 12 | import skimage.transform as trans 13 | import numpy as np 14 | from keras.models import * 15 | from keras.layers import * 16 | from keras.optimizers import * 17 | from keras.callbacks import ModelCheckpoint, LearningRateScheduler 18 | from keras import backend as keras 19 | 20 | def res_block(in_put, n_kernels): 21 | 22 | conv = BatchNormalization(axis=3)(in_put) 23 | conv = Conv2D(n_kernels, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv) 24 | conv = BatchNormalization(axis=3)(conv) 25 | conv = Conv2D(n_kernels, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv) 26 | 27 | conv_shortcut = Conv2D(n_kernels, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(in_put) 28 | conv_shortcut = BatchNormalization(axis=3)(conv_shortcut) 29 | 30 | conv = Add()([conv, conv_shortcut]) 31 | 32 | return conv 33 | 34 | def unet(pretrained_weights = None,input_size = (512,512,1)): 35 | inputs = Input(input_size) 36 | 37 | # Level 1 38 | conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(inputs) 39 | conv1 = BatchNormalization(axis=3)(conv1) 40 | conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv1) 41 | conv1 = BatchNormalization(axis=3)(conv1) 42 | conv1 = Add()([conv1, inputs]) 43 | 44 | pool1 = MaxPooling2D(pool_size=(2, 2))(conv1) 45 | 46 | # Level 2 47 | conv2 = res_block(pool1, n_kernels = 128) 48 | pool2 = MaxPooling2D(pool_size=(2, 2))(conv2) 49 | 50 | # Level 3 51 | conv3 = res_block(pool2, n_kernels = 256) 52 | pool3 = MaxPooling2D(pool_size=(2, 2))(conv3) 53 | 54 | # Level 4 55 | conv4 = res_block(pool3, n_kernels = 512) 56 | drop4 = Dropout(0.5)(conv4) 57 | pool4 = MaxPooling2D(pool_size=(2, 2))(drop4) 58 | 59 | # Level 5 60 | conv5 = res_block(pool4, n_kernels = 1024) 61 | drop5 = Dropout(0.5)(conv5) 62 | 63 | # Level 6 64 | up6 = Conv2D(512, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(drop5)) 65 | merge6 = concatenate([drop4,up6], axis = 3) 66 | conv6 = res_block(merge6, n_kernels = 512) 67 | 68 | # Level 7 69 | up7 = Conv2D(256, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv6)) 70 | merge7 = concatenate([conv3, up7], axis = 3) 71 | conv7 = res_block(merge7, n_kernels = 256) 72 | 73 | # Level 8 74 | up8 = Conv2D(128, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv7)) 75 | merge8 = concatenate([conv2, up8], axis = 3) 76 | conv8 = res_block(merge8, n_kernels = 128) 77 | 78 | # Level 9 79 | up9 = Conv2D(64, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv8)) 80 | merge9 = concatenate([conv1, up9], axis = 3) 81 | conv9 = res_block(merge9, n_kernels = 64) 82 | conv9 = Conv2D(2, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9) 83 | 84 | conv10 = Conv2D(1, 1, activation = 'sigmoid')(conv9) 85 | 86 | model = Model(input = inputs, output = conv10) 87 | 88 | model.compile(optimizer = Adam(lr = 1e-4), loss = 'binary_crossentropy', metrics = ['accuracy']) 89 | 90 | model.summary() 91 | 92 | if(pretrained_weights): 93 | model.load_weights(pretrained_weights) 94 | 95 | return model 96 | -------------------------------------------------------------------------------- /model_resunet.py: -------------------------------------------------------------------------------- 1 | # Sept. 2019 2 | # Tran Le Anh, MSc Student 3 | # Satellite Image Processing Lab, Myongji Univ., Yongin, South Korea 4 | # tranleanh.nt@gmail.com 5 | # https://sites.google.com/view/leanhtran 6 | 7 | # Residual Unet Model (Architecture) 8 | 9 | import numpy as np 10 | import os 11 | import skimage.io as io 12 | import skimage.transform as trans 13 | import numpy as np 14 | from keras.models import * 15 | from keras.layers import * 16 | from keras.optimizers import * 17 | from keras.callbacks import ModelCheckpoint, LearningRateScheduler 18 | from keras import backend as keras 19 | 20 | def res_block(in_put, n_kernels): 21 | 22 | conv = BatchNormalization(axis=3)(in_put) 23 | conv = Activation('relu')(conv) 24 | conv = Conv2D(n_kernels, 3, padding = 'same', kernel_initializer = 'he_normal')(conv) 25 | conv = BatchNormalization(axis=3)(conv) 26 | conv = Activation('relu')(conv) 27 | conv = Conv2D(n_kernels, 3, padding = 'same', kernel_initializer = 'he_normal')(conv) 28 | 29 | conv_shortcut = Conv2D(n_kernels, 3, padding = 'same', kernel_initializer = 'he_normal')(in_put) 30 | conv_shortcut = BatchNormalization(axis=3)(conv_shortcut) 31 | 32 | conv = Add()([conv, conv_shortcut]) 33 | 34 | return conv 35 | 36 | def unet(pretrained_weights = None,input_size = (512,512,1)): 37 | inputs = Input(input_size) 38 | 39 | # Level 1 40 | conv1 = BatchNormalization(axis=3)(inputs) 41 | conv1 = Activation('relu')(conv1) 42 | conv1 = Conv2D(64, 3, padding = 'same', kernel_initializer = 'he_normal')(conv1) 43 | 44 | conv1_shortcut = Conv2D(64, 3, padding = 'same', kernel_initializer = 'he_normal')(inputs) 45 | conv1_shortcut = BatchNormalization(axis=3)(conv1_shortcut) 46 | conv1 = Add()([conv1, conv1_shortcut]) 47 | 48 | pool1 = MaxPooling2D(pool_size=(2, 2))(conv1) 49 | 50 | # Level 2 51 | conv2 = res_block(pool1, n_kernels = 128) 52 | pool2 = MaxPooling2D(pool_size=(2, 2))(conv2) 53 | 54 | # Level 3 55 | conv3 = res_block(pool2, n_kernels = 256) 56 | pool3 = MaxPooling2D(pool_size=(2, 2))(conv3) 57 | 58 | # Level 4 - Bridge 59 | conv4 = res_block(pool3, n_kernels = 512) 60 | 61 | # Level 5 62 | up5 = Conv2D(256, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv4)) 63 | merge5 = concatenate([conv3,up5], axis = 3) 64 | conv5 = res_block(merge5, n_kernels = 256) 65 | 66 | # Level 6 67 | up6 = Conv2D(128, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv5)) 68 | merge6 = concatenate([conv2, up6], axis = 3) 69 | conv6 = res_block(merge6, n_kernels = 128) 70 | 71 | # Level 7 72 | up7 = Conv2D(64, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv6)) 73 | merge7 = concatenate([conv1, up7], axis = 3) 74 | conv7 = res_block(merge7, n_kernels = 64) 75 | 76 | out_put = Conv2D(1, 1, activation = 'sigmoid')(conv7) 77 | 78 | model = Model(input = inputs, output = out_put) 79 | 80 | model.compile(optimizer = Adam(lr = 1e-4), loss = 'binary_crossentropy', metrics = ['accuracy']) 81 | 82 | model.summary() 83 | 84 | if(pretrained_weights): 85 | model.load_weights(pretrained_weights) 86 | 87 | return model 88 | -------------------------------------------------------------------------------- /model_unet.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | import skimage.io as io 4 | import skimage.transform as trans 5 | import numpy as np 6 | from keras.models import * 7 | from keras.layers import * 8 | from keras.optimizers import * 9 | from keras.callbacks import ModelCheckpoint, LearningRateScheduler 10 | from keras import backend as keras 11 | 12 | 13 | def unet(pretrained_weights = None,input_size = (512,512,1)): 14 | inputs = Input(input_size) 15 | conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(inputs) 16 | conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv1) 17 | pool1 = MaxPooling2D(pool_size=(2, 2))(conv1) 18 | conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool1) 19 | conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv2) 20 | pool2 = MaxPooling2D(pool_size=(2, 2))(conv2) 21 | conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool2) 22 | conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv3) 23 | pool3 = MaxPooling2D(pool_size=(2, 2))(conv3) 24 | conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool3) 25 | conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv4) 26 | drop4 = Dropout(0.5)(conv4) 27 | pool4 = MaxPooling2D(pool_size=(2, 2))(drop4) 28 | 29 | conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool4) 30 | conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv5) 31 | drop5 = Dropout(0.5)(conv5) 32 | 33 | up6 = Conv2D(512, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(drop5)) 34 | merge6 = concatenate([drop4,up6], axis = 3) 35 | conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge6) 36 | conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv6) 37 | 38 | up7 = Conv2D(256, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv6)) 39 | merge7 = concatenate([conv3,up7], axis = 3) 40 | conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge7) 41 | conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv7) 42 | 43 | up8 = Conv2D(128, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv7)) 44 | merge8 = concatenate([conv2,up8], axis = 3) 45 | conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge8) 46 | conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv8) 47 | 48 | up9 = Conv2D(64, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(conv8)) 49 | merge9 = concatenate([conv1,up9], axis = 3) 50 | conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge9) 51 | conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9) 52 | conv9 = Conv2D(2, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9) 53 | conv10 = Conv2D(1, 1, activation = 'sigmoid')(conv9) 54 | 55 | model = Model(input = inputs, output = conv10) 56 | 57 | model.compile(optimizer = Adam(lr = 1e-4), loss = 'binary_crossentropy', metrics = ['accuracy']) 58 | 59 | model.summary() 60 | 61 | if(pretrained_weights): 62 | model.load_weights(pretrained_weights) 63 | 64 | return model 65 | 66 | 67 | -------------------------------------------------------------------------------- /resize_tool.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import glob 3 | import os 4 | 5 | img = cv2.imread("./7x7/02_7341_7x7.png") 6 | img = cv2.resize(img, (1572, 1572)) 7 | 8 | cv2.imshow("Resized Image", img) 9 | cv2.imwrite('resized_1572.png', img) 10 | cv2.waitKey() 11 | cv2.destroyAllWindows() -------------------------------------------------------------------------------- /rgb2gray.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import glob 3 | import os 4 | 5 | srcImageName = glob.glob("./gen_input/*.jpg") 6 | 7 | for imgName in srcImageName: 8 | img = cv2.imread(imgName) 9 | img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 10 | 11 | name = os.path.basename(imgName) 12 | print(name) 13 | 14 | cv2.imwrite(name, img) 15 | 16 | # Convert to binary 17 | # ret, bw_img = cv2.threshold(img, 50, 255, cv2.THRESH_BINARY) 18 | 19 | # name = os.path.basename(imgName) 20 | # print(name) 21 | 22 | # cv2.imshow("Binary Image", bw_img) 23 | # cv2.imwrite(name, bw_img) 24 | 25 | if cv2.waitKey(20) == 27: 26 | break 27 | 28 | # cv2.destroyAllWindows() -------------------------------------------------------------------------------- /test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tranleanh/modified-resunet/e170ba75fe4a66757831b4b24b4f0712ab41ce97/test.png -------------------------------------------------------------------------------- /tmp_task.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tranleanh/modified-resunet/e170ba75fe4a66757831b4b24b4f0712ab41ce97/tmp_task.py --------------------------------------------------------------------------------