├── README.md ├── generator.py ├── model.py ├── predict.py ├── test_ts_norm128Colab.05-1.13.hdf5 └── test_ts_norm32PC.05-1.08.hdf5 /README.md: -------------------------------------------------------------------------------- 1 | # SceneChangeDetectionNet 2 | Siamese neural network for finding differences between two pictures. 3 | -------------------------------------------------------------------------------- /generator.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Feb 23 16:48:18 2019 4 | 5 | @author: User 6 | """ 7 | 8 | import numpy as np 9 | #import cv2 10 | from keras.utils import Sequence 11 | from PIL import Image 12 | DataPath = './dataset/' 13 | Hsize = 512 14 | Wsize = 512 15 | Means = [[107.800, 117.692, 119.979], [110.655, 117.107, 119.135]] 16 | 17 | 18 | def preprocessing(img, mean): 19 | img = img.resize((Wsize, Hsize), Image.ANTIALIAS) 20 | if mean[0] == 0: 21 | img = img.resize((472, 472), Image.ANTIALIAS) 22 | return np.array(img.convert('RGB'), dtype='float') / 255 23 | else: 24 | temp = np.array(img.convert('RGB'), dtype='float') 25 | l1 = temp.shape[0] 26 | l2 = temp.shape[1] 27 | for i in range(l1): 28 | for j in range(l2): 29 | temp[i][j] -= mean 30 | return temp 31 | 32 | 33 | class DataGenerator(Sequence): 34 | def __init__(self, patches, batchSize=32, flag=0): 35 | self.patches = patches 36 | self.numb = 0 37 | self.batchSize = batchSize 38 | self.X = [] 39 | for j in range(self.batchSize): 40 | temp = [] 41 | for p in self.patches: 42 | f = open(p) 43 | lines = f.readlines() 44 | buf = Image.open(DataPath + lines[self.numb][:-1]) 45 | if self.patches.index(p) == 0: 46 | buf = preprocessing(buf, np.array(Means[0])) 47 | elif self.patches.index(p) == 1: 48 | buf = preprocessing(buf, np.array(Means[1])) 49 | else: 50 | buf = preprocessing(buf, [0, 0, 0]) 51 | temp.append(buf) 52 | self.numb += 1 53 | self.X.append(temp) 54 | 55 | def __len__(self): 56 | return self.batchSize 57 | 58 | def __getitem__(self, index): 59 | x = [np.array([self.X[index][0]]), np.array([self.X[index][1]])] 60 | #x1 = np.array(self.X[index][0]) 61 | #x2 = np.array(self.X[index][1]) 62 | y = np.array([self.X[index][-1]]) 63 | return x, y 64 | 65 | def on_epoch_end(self): 66 | self.X = [] 67 | for j in range(self.batchSize): 68 | temp = [] 69 | for p in self.patches: 70 | f = open(p) 71 | lines = f.readlines() 72 | buf = Image.open(DataPath + lines[self.numb][:-1]) 73 | if self.patches.index(p) == 0: 74 | buf = preprocessing(buf, np.array(Means[0])) 75 | elif self.patches.index(p) == 1: 76 | buf = preprocessing(buf, np.array(Means[1])) 77 | else: 78 | buf = preprocessing(buf, [0, 0, 0]) 79 | temp.append(buf) 80 | self.numb += 1 81 | self.X.append(temp) 82 | 83 | 84 | def main(): 85 | ''' 86 | path = './dataset2014/dataset/dynamicBackground/boats/input/in007157.jpg' 87 | img = Image.open(path) 88 | print(np.array(img.convert('RGB')), np.array(img.convert('RGB')).shape) 89 | ''' 90 | ''' 91 | patches_train = ['t0train.txt', 't1train.txt', 'gttrain.txt'] 92 | patches_val = ['t0val.txt', 't1val.txt', 'gtval.txt'] 93 | dg = DataGenerator(patches_train) 94 | for i in range(10): 95 | leng = dg.__len__() 96 | for j in range(leng): 97 | x, y = dg.__getitem__(j) 98 | print(x.shape, y.shape) 99 | print(x, y) 100 | ''' 101 | ''' 102 | f = open('val.txt') 103 | i = 1 104 | t0 = open('t0val.txt', 'w') 105 | t1 = open('t1val.txt', 'w') 106 | gt = open('gtval.txt', 'w') 107 | for line in f: 108 | buf = line.split() 109 | t0.write(buf[0] + '\n') 110 | t1.write(buf[1] + '\n') 111 | gt.write(buf[2] + '\n') 112 | if i == 1: 113 | buf = line.split() 114 | print(buf) 115 | print(line) 116 | i = 0 117 | ''' 118 | return 0 119 | 120 | 121 | main() -------------------------------------------------------------------------------- /model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Feb 23 23:47:44 2019 4 | 5 | @author: User 6 | """ 7 | 8 | import tensorflow as tf 9 | import numpy as np 10 | import keras.backend as K 11 | from tensorflow.math import reduce_max 12 | from generator import * 13 | from keras.layers import Input, Conv2D, MaxPooling2D, Dense, Flatten, Lambda, UpSampling2D, BatchNormalization 14 | from keras.models import Sequential, Model 15 | from keras import regularizers 16 | from keras import optimizers 17 | from keras.callbacks import ModelCheckpoint 18 | from scipy.misc import imresize 19 | #from keras.utils import normalize 20 | from tensorflow.linalg import norm 21 | import matplotlib.pyplot as plt 22 | import pandas as pd 23 | 24 | 25 | def toTarget(tensor): 26 | buf1 = reduce_max(K.flatten(tensor)) 27 | print(buf1) 28 | buf2 = tensor 29 | return imresize(buf2 / buf1, 8.0) 30 | #return imresize(np.array(tensor, dtype='float') / float(K.max(K.flatten(tensor))), 8.0) 31 | 32 | 33 | def normal(tensors): 34 | tensor = K.sqrt(K.square(tensors[0]) - K.square(tensors[1])) 35 | tensor = tf.div(tf.subtract(tensor, tf.reduce_min(tensor)), tf.subtract(tf.reduce_max(tensor), tf.reduce_min(tensor))) 36 | return tensor 37 | 38 | 39 | def get_siamese_model(input_shape): 40 | """ 41 | Model architecture 42 | """ 43 | 44 | # Define the tensors for the two input images 45 | left_input = Input(input_shape) 46 | right_input = Input(input_shape) 47 | 48 | # Convolutional Neural Network 49 | model = Sequential() 50 | model.add(Conv2D(16, (10, 10), activation='sigmoid', input_shape=input_shape, #64 51 | kernel_regularizer=regularizers.l2(2e-4))) 52 | model.add(MaxPooling2D(pool_size=(2, 2), padding='same')) 53 | model.add(Conv2D(8, (7, 7), #128 54 | kernel_regularizer=regularizers.l2(2e-4))) 55 | model.add(MaxPooling2D(pool_size=(2, 2), padding='same')) 56 | model.add(Conv2D(4, (4, 4), #128 57 | kernel_regularizer=regularizers.l2(2e-4))) 58 | model.add(MaxPooling2D(pool_size=(2, 2), padding='same')) 59 | model.add(Conv2D(3, (2, 2), 60 | kernel_regularizer=regularizers.l2(2e-4))) 61 | #model.add(Conv2D(3, (4, 4), activation='relu', 62 | # kernel_regularizer=regularizers.l2(2e-4))) 63 | # Generate the encodings (feature vectors) for the two images 64 | encoded_l = model(left_input) 65 | encoded_r = model(right_input) 66 | # Add a customized layer to compute the absolute difference between the encodings 67 | #L2_layer = Lambda(lambda tensors:(normal(tensors)))#K.sqrt(K.square(tensors[0]) - K.square(tensors[1])) / norm(K.sqrt(K.square(tensors[0]) - K.square(tensors[1])), ord=1)) 68 | #L2_layer = Lambda(normal, arguments=tensors) 69 | #L2_layer = Lambda(lambda tensors:K.normalize_batch_in_training( K.sqrt(K.square(tensors[0]) - K.square(tensors[1])), 70 | # K.ones_like(K.sqrt(K.square(tensors[0]) - K.square(tensors[1]))), 71 | # K.zeros_like(K.sqrt(K.square(tensors[0]) - K.square(tensors[1]))) )) 72 | L2_layer = Lambda(lambda tensors:(K.sqrt(K.square(tensors[0]) - K.square(tensors[1])))) 73 | L2_distance = L2_layer([encoded_l, encoded_r]) 74 | UpLayer = UpSampling2D(size=(8, 8))(L2_distance) 75 | NormLayer = BatchNormalization()(UpLayer) 76 | #UpLayer = Lambda(lambda tensor:toTarget(tensor))(L2_distance) 77 | # Add a dense layer with a sigmoid unit to generate the similarity score 78 | #prediction = Dense(1,activation='sigmoid')(L2_distance) 79 | 80 | # Connect the inputs with the outputs 81 | siamese_net = Model(inputs=[left_input,right_input],outputs=UpLayer) 82 | # return the model 83 | return siamese_net 84 | 85 | 86 | def main(): 87 | inp_shape = (512, 512, 3) 88 | patches_train = ['t0train.txt', 't1train.txt', 'gttrain.txt'] 89 | patches_val = ['t0val.txt', 't1val.txt', 'gtval.txt'] 90 | dg = DataGenerator(patches_train) 91 | valid = DataGenerator(patches_val) 92 | model = get_siamese_model(inp_shape) 93 | check = ModelCheckpoint('testn.{epoch:02d}-{val_loss:.2f}.hdf5', period=1) 94 | model.compile(loss='mae', optimizer=optimizers.Adam(lr=0.0001, decay=0.0001), metrics=['accuracy']) 95 | history = model.fit_generator(generator=dg, steps_per_epoch=32, epochs=50, verbose=1, callbacks=[check], 96 | validation_data=valid) 97 | plt.plot(history.history['loss']) 98 | plt.plot(history.history['val_loss']) 99 | df = pd.DataFrame.from_dict(history.history) 100 | df.to_csv('test.csv', index=False, index_label=False) 101 | 102 | return 0 103 | 104 | 105 | main() 106 | -------------------------------------------------------------------------------- /predict.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Feb 24 05:26:06 2019 4 | 5 | @author: User 6 | """ 7 | 8 | import numpy as np 9 | from generator import * 10 | from PIL import Image 11 | from keras.models import load_model 12 | #from tensorflow import norm 13 | 14 | 15 | def main(): 16 | model_path = 'testn.01-0.05.hdf5' 17 | path1 = 'C:\Download\SevStal\TESTSET\IMG_20190223_140201.jpg' 18 | path2 = 'C:\Download\SevStal\TESTSET\IMG_20190223_140210.jpg' 19 | tpath = '‪C:\Download\SevStal\dataset\PTZ\twoPositionPTZCam\gt_binary\gt001386.png' 20 | buf1 = preprocessing(Image.open(path1), np.array(Means[0])) 21 | buf2 = preprocessing(Image.open(path2), np.array(Means[1])) 22 | targ = preprocessing(Image.open(path2), [0, 0, 0]) 23 | print(targ, np.max(targ)) 24 | X = [np.array([buf1]), np.array([buf2])] 25 | 26 | model = load_model(model_path) 27 | Y = model.predict(X) 28 | print(Y, np.max(Y / np.max(Y))) 29 | Y = Y /np.max(Y) 30 | Y = np.array(Y * 255, dtype='uint8')[0] 31 | print(Y, Y.shape) 32 | img = Image.fromarray(Y) 33 | print(img) 34 | img.save('test.png') 35 | 36 | return 0 37 | 38 | 39 | main() 40 | -------------------------------------------------------------------------------- /test_ts_norm128Colab.05-1.13.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glkuzi/SceneChangeDetectionNet/522864c53bee28e97cba4be957b2f3d095fea201/test_ts_norm128Colab.05-1.13.hdf5 -------------------------------------------------------------------------------- /test_ts_norm32PC.05-1.08.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/glkuzi/SceneChangeDetectionNet/522864c53bee28e97cba4be957b2f3d095fea201/test_ts_norm32PC.05-1.08.hdf5 --------------------------------------------------------------------------------