├── README.md ├── __init__.py ├── data └── README.md ├── image_sample.PNG ├── preprocess_file.py ├── processed_data └── README.md ├── run_code.py ├── sample_output.PNG └── unet_model.py /README.md: -------------------------------------------------------------------------------- 1 | # Building_detection 2 | This project is to identify buildings in satellite using Unet and masking method 3 | 4 | To execute this project, please follow the below guidlines 5 | 6 | 1: run_code.py is the main file 7 | 8 | 2: save images and polygon files in images and polygon folders under data 9 | 10 | 3: ProcessFile class is invoked in run_code that creates csv files of images and masks with user define pixel values like 128, 256 etc. 11 | 12 | 4: Processed image csv and mask csv files are used in main code to build model 13 | 14 | # references 15 | https://lmb.informatik.uni-freiburg.de/people/ronneber/u-net/ 16 | 17 | https://github.com/zhixuhao/unet 18 | 19 | https://arxiv.org/pdf/1602.06564v1.pdf 20 | 21 | http://cs229.stanford.edu/proj2017/final-reports/5243715.pdf 22 | 23 | # output 24 | ![Sample Output](https://github.com/statisticalplumber/Building_detection/blob/master/sample_output.PNG) 25 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/statisticalplumber/Building_detection/950889c701581198f251b912b633690ce8e01336/__init__.py -------------------------------------------------------------------------------- /data/README.md: -------------------------------------------------------------------------------- 1 | # Building_detection 2 | Here user keeps raw image data in images folder and polygon text files in polygon folder 3 | -------------------------------------------------------------------------------- /image_sample.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/statisticalplumber/Building_detection/950889c701581198f251b912b633690ce8e01336/image_sample.PNG -------------------------------------------------------------------------------- /preprocess_file.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import os 4 | from keras.preprocessing import image 5 | import cv2 6 | 7 | 8 | class ProcessFile: 9 | def __init__(self, loc_image, loc_poly, orignal_image_size): 10 | self.loc_image = loc_image 11 | self.loc_poly = loc_poly 12 | self.image_poly_df = self.get_image_poly_dataframe() 13 | self.orignal_image_size = orignal_image_size 14 | 15 | def get_image_poly_dataframe(self): 16 | ind_img = os.listdir(self.loc_image) 17 | ind_poly = os.listdir(self.loc_poly) 18 | l_image = pd.DataFrame(ind_img, index=[i.split('.')[0] for i in ind_img]) 19 | l_poly = pd.DataFrame(ind_poly, [i.split('.')[0] for i in ind_poly]) 20 | d_img = pd.concat([l_image, l_poly], axis=1) 21 | d_img = d_img.dropna() 22 | d_img.columns = ['image', 'poly'] 23 | return d_img 24 | 25 | def get_custom_image_mask(self, size=[128,128]): 26 | d_img = self.image_poly_df 27 | l_image_file = [] 28 | l_polygon = [] 29 | for count, img, poly in zip(range(len(d_img)), d_img.image, d_img.poly): 30 | try: 31 | m = image.img_to_array(image.load_img(self.loc_image + img, grayscale=True).resize(size)) 32 | l_image_file.append(m.reshape([len(m) ** 2])) 33 | dp = pd.read_table(self.loc_poly, header=None) 34 | xx = [eval(i) for i in dp.iloc[:, 0].tolist()] 35 | im = np.zeros([len(m)] * 2, dtype=np.uint8) 36 | for i in xx: 37 | a3 = np.array([i], dtype=np.int32) //(self.orignal_image_size/len(m)) 38 | im = cv2.fillPoly(im, a3, 255) 39 | l_polygon.append(im.reshape([len(m) ** 2])) 40 | except: 41 | pass 42 | df_image = pd.DataFrame(l_image_file) 43 | df_mask = pd.DataFrame(l_polygon) 44 | return df_image, df_mask 45 | 46 | if __name__=="__main__": 47 | 48 | loc_image = './data/images/' 49 | loc_poly = './data/polygon/' 50 | pfile = ProcessFile(loc_image, loc_poly, 1280) 51 | processed_image_df, mask_df = pfile.get_custom_image_mask(size=[128, 128]) 52 | 53 | processed_image_df.to_csv('./data/train/mask_file.csv', index=False) 54 | mask_df.to_csv('./data/train/train_file.csv', index=False) 55 | 56 | -------------------------------------------------------------------------------- /processed_data/README.md: -------------------------------------------------------------------------------- 1 | # Building_detection 2 | This folder will contain csv files of image pixels and mask pixels 3 | 4 | These csv file will be generated from process_file script 5 | -------------------------------------------------------------------------------- /run_code.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Sep 30 22:27:08 2018 4 | 5 | @author: kr 6 | """ 7 | 8 | import tensorflow as tf 9 | import keras 10 | from keras.callbacks import ModelCheckpoint, LearningRateScheduler 11 | import pandas as pd 12 | from unet_model import UnetModel 13 | import matplotlib.pyplot as plt 14 | from sklearn.model_selection import train_test_split 15 | from preprocess_file import ProcessFile 16 | 17 | # these snippets are used to invoke GPU if available or no of cpu cores 18 | config = tf.ConfigProto( device_count = {'GPU': 1 , 'CPU': 8} ) 19 | sess = tf.Session(config=config) 20 | keras.backend.set_session(sess) 21 | 22 | ############################################################ 23 | # Creating CSV file of image pixels and mask pixels 24 | loc_image = './data/images/' # location of raw image 25 | loc_poly = './data/polygon/' # Location of polygon files 26 | 27 | pfile = ProcessFile(loc_image, loc_poly, 1280) 28 | processed_image_df, mask_df = pfile.get_custom_image_mask(size=[128, 128]) 29 | 30 | # save file to some location for feeding in neural network 31 | processed_image_df.to_csv('./processed_data/mask_file.csv', index=False) 32 | mask_df.to_csv('./processed_data/train_file.csv', index=False) 33 | 34 | ############################################################# 35 | 36 | ## reading data that has been processed to 128 pixel size and its mask 37 | df_image = pd.read_csv('./processed_data/image_file.csv') 38 | df_mask = pd.read_csv('./processed_data/mask_file.csv') 39 | 40 | # scaling data 41 | image_array=df_image.values.reshape([len(df_image),128,128,1])/255. 42 | mask_array=df_mask.values.reshape([len(df_image),128,128,1])/255. 43 | 44 | # spliting data for train and test 45 | x_train, x_test, y_train, y_test = train_test_split(image_array, mask_array, test_size=0.1) 46 | 47 | # define the size of image prepared through preprocessing 48 | row_size = 128 49 | col_size = 128 50 | 51 | # calling unet model class 52 | unet = UnetModel(row_size, col_size, 1) 53 | 54 | model = unet.small_unet() 55 | model_checkpoint = ModelCheckpoint('unet_bd.hdf5', monitor='loss',verbose=1, save_best_only=True) 56 | # training the model 57 | model.fit(x_train, y_train, batch_size=64, nb_epoch=10, verbose=1, shuffle=True, 58 | validation_split=0.2, callbacks=[model_checkpoint]) 59 | 60 | # predicting on test set 61 | res = model.predict(x_test) 62 | 63 | 64 | # simplot to view output 65 | %matplotlib 66 | i=2 67 | plt.figure(figsize=(1,3)) 68 | plt.subplot(1,3,1) 69 | plt.imshow((x_test[i,:]).reshape([128,128])) 70 | plt.title('Image View at 128 pixel size') 71 | plt.subplot(1,3,2) 72 | plt.imshow((res[i,:]).reshape([128,128])) 73 | plt.title('pridicted mask View at 128 pixel size') 74 | plt.subplot(1,3,3) 75 | plt.imshow(res[i,:].reshape([128,128])>res[i,:].reshape([128,128]).mean()) 76 | plt.title('pridicted mask View after setting threshold mean at 128 pixel size') 77 | plt.show() 78 | 79 | 80 | # method to predict mask of new image 81 | image_path = 'image_sample.png' 82 | 83 | def predict_image_mask(image_path, size): 84 | img_obj = image.load_img(image_path, grayscale=True).resize(size=[size]*2) 85 | arr_obj = image.img_to_array(img_obj) 86 | arr_obj = pd.np.expand_dims(arr_obj, axis=0) 87 | new_res = model.predict(arr_obj) 88 | return arr_obj.reshape([size]*2), new_res.reshape([size]*2) 89 | 90 | out = predict_image_mask(image_path, 128) 91 | %matplotlib 92 | plt.figure(figsize=(1,3)) 93 | plt.subplot(1,3,1) 94 | plt.imshow(out[0]) 95 | plt.title('Image View at 128 pixel size') 96 | plt.subplot(1,3,2) 97 | plt.imshow(out[1]) 98 | plt.title('pridicted mask View at 128 pixel size') 99 | plt.subplot(1,3,3) 100 | plt.imshow(out[1]>out[1].mean()*1.02) 101 | plt.title('pridicted mask View after setting threshold mean at 128 pixel size') 102 | plt.show() 103 | 104 | -------------------------------------------------------------------------------- /sample_output.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/statisticalplumber/Building_detection/950889c701581198f251b912b633690ce8e01336/sample_output.PNG -------------------------------------------------------------------------------- /unet_model.py: -------------------------------------------------------------------------------- 1 | from keras.models import * 2 | from keras.layers import * 3 | from keras.optimizers import * 4 | from keras.callbacks import ModelCheckpoint, LearningRateScheduler 5 | from keras import backend as K 6 | 7 | row_size = 128 8 | col_size = 128 9 | color_channel = 1 10 | 11 | class UnetModel: 12 | def __init__(self, row_size, col_size, color_channel): 13 | self.row_size = row_size 14 | self.col_size = col_size 15 | self.color_channel = color_channel 16 | self.smooth = 1. 17 | 18 | 19 | def accuracy_iou(self, y_true, y_pred): 20 | y_true_f = K.flatten(y_true) 21 | y_pred_f = K.flatten(y_pred) 22 | intersection = K.sum(y_true_f * y_pred_f) 23 | return (2. * intersection + self.smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + self.smooth) 24 | 25 | 26 | def loss_iou(self, y_true, y_pred): 27 | return -self.accuracy_iou(y_true, y_pred) 28 | 29 | 30 | def small_unet(self): 31 | inputs = Input((self.row_size, self.col_size, self.color_channel)) 32 | conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(inputs) 33 | conv1 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv1) 34 | pool1 = MaxPooling2D(pool_size=(2, 2))(conv1) 35 | 36 | conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(pool1) 37 | conv2 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv2) 38 | pool2 = MaxPooling2D(pool_size=(2, 2))(conv2) 39 | 40 | conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(pool2) 41 | conv3 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv3) 42 | pool3 = MaxPooling2D(pool_size=(2, 2))(conv3) 43 | 44 | conv4 = Conv2D(256, (3, 3), activation='relu', padding='same')(pool3) 45 | conv4 = Conv2D(256, (3, 3), activation='relu', padding='same')(conv4) 46 | drop4 = Dropout(0.5)(conv4) 47 | pool4 = MaxPooling2D(pool_size=(2, 2))(drop4) 48 | 49 | conv5 = Conv2D(512, (3, 3), activation='relu', padding='same')(pool4) 50 | conv5 = Conv2D(512, (3, 3), activation='relu', padding='same')(conv5) 51 | drop5 = Dropout(0.5)(conv5) 52 | 53 | up6 = concatenate([Conv2DTranspose(256, (2, 2), strides=(2, 2), padding='same')(drop5), conv4], axis=3) 54 | conv6 = Conv2D(256, (3, 3), activation='relu', padding='same')(up6) 55 | conv6 = Conv2D(256, (3, 3), activation='relu', padding='same')(conv6) 56 | 57 | up7 = concatenate([Conv2DTranspose(128, (2, 2), strides=(2, 2), padding='same')(conv6), conv3], axis=3) 58 | conv7 = Conv2D(128, (3, 3), activation='relu', padding='same')(up7) 59 | conv7 = Conv2D(128, (3, 3), activation='relu', padding='same')(conv7) 60 | drop7 = Dropout(0.5)(conv7) 61 | 62 | up8 = concatenate([Conv2DTranspose(64, (2, 2), strides=(2, 2), padding='same')(drop7), conv2], axis=3) 63 | conv8 = Conv2D(64, (3, 3), activation='relu', padding='same')(up8) 64 | conv8 = Conv2D(64, (3, 3), activation='relu', padding='same')(conv8) 65 | 66 | up9 = concatenate([Conv2DTranspose(32, (2, 2), strides=(2, 2), padding='same')(conv8), conv1], axis=3) 67 | conv9 = Conv2D(32, (3, 3), activation='relu', padding='same')(up9) 68 | conv9 = Conv2D(32, (3, 3), activation='relu', padding='same')(conv9) 69 | drop9 = Dropout(0.5)(conv9) 70 | 71 | conv10 = Conv2D(1, (1, 1), activation='sigmoid')(drop9) 72 | 73 | model = Model(inputs=[inputs], outputs=[conv10]) 74 | 75 | model.compile(optimizer=Adam(lr=1e-5), loss=self.loss_iou, metrics=[self.accuracy_iou]) 76 | #model.compile(optimizer=Adam(lr=1e-4), loss='binary_crossentropy', metrics=['accuracy']) 77 | 78 | return model 79 | 80 | 81 | def full_unet(self, pretrained_weights=None): 82 | inputs = Input((self.row_size, self.col_size, self.color_channel)) 83 | conv1 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(inputs) 84 | conv1 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv1) 85 | pool1 = MaxPooling2D(pool_size=(2, 2))(conv1) 86 | conv2 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool1) 87 | conv2 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv2) 88 | pool2 = MaxPooling2D(pool_size=(2, 2))(conv2) 89 | conv3 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool2) 90 | conv3 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv3) 91 | pool3 = MaxPooling2D(pool_size=(2, 2))(conv3) 92 | conv4 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool3) 93 | conv4 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv4) 94 | drop4 = Dropout(0.5)(conv4) 95 | pool4 = MaxPooling2D(pool_size=(2, 2))(drop4) 96 | conv5 = Conv2D(1024, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool4) 97 | conv5 = Conv2D(1024, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv5) 98 | drop5 = Dropout(0.5)(conv5) 99 | 100 | up6 = Conv2DTranspose(512, (2, 2), strides=(2, 2), activation='relu', padding='same', 101 | kernel_initializer='he_normal')(drop5) 102 | merge6 = concatenate([drop4, up6], axis=3) 103 | conv6 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge6) 104 | conv6 = Conv2D(512, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv6) 105 | 106 | up7 = Conv2DTranspose(256, (2, 2), strides=(2, 2), activation='relu', padding='same', 107 | kernel_initializer='he_normal')(conv6) 108 | merge7 = concatenate([conv3, up7], axis=3) 109 | conv7 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge7) 110 | conv7 = Conv2D(256, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv7) 111 | 112 | up8 = Conv2DTranspose(128, (2, 2), strides=(2, 2), activation='relu', padding='same', 113 | kernel_initializer='he_normal')(conv7) 114 | merge8 = concatenate([conv2, up8], axis=3) 115 | conv8 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge8) 116 | conv8 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv8) 117 | 118 | up9 = Conv2DTranspose(64, (2, 2), strides=(2, 2), activation='relu', padding='same', 119 | kernel_initializer='he_normal')(conv8) 120 | merge9 = concatenate([conv1, up9], axis=3) 121 | conv9 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(merge9) 122 | conv9 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv9) 123 | conv9 = Conv2D(2, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv9) 124 | conv10 = Conv2D(1, 1, activation='sigmoid')(conv9) 125 | 126 | model = Model(input=inputs, output=conv10) 127 | 128 | model.compile(optimizer=Adam(lr=1e-4), loss='binary_crossentropy', metrics=['accuracy']) 129 | 130 | # model.summary() 131 | 132 | if (pretrained_weights): 133 | model.load_weights(pretrained_weights) 134 | 135 | return model 136 | 137 | --------------------------------------------------------------------------------