├── Detection ├── Custom │ ├── __init__.py │ ├── callbacks.py │ ├── evaluate.py │ ├── gen_anchors.py │ ├── generator.py │ ├── utils │ │ ├── __init__.py │ │ ├── bbox.py │ │ ├── colors.py │ │ ├── image.py │ │ ├── multi_gpu_model.py │ │ └── utils.py │ ├── voc.py │ └── yolo.py ├── YOLOv3 │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-35.pyc │ │ ├── models.cpython-35.pyc │ │ └── utils.cpython-35.pyc │ ├── models.py │ └── utils.py ├── __init__.py ├── __pycache__ │ └── __init__.cpython-35.pyc ├── keras_resnet │ ├── __init__.py │ ├── __pycache__ │ │ └── __init__.cpython-35.pyc │ ├── benchmarks │ │ └── __init__.py │ ├── blocks │ │ ├── _1d.py │ │ ├── _2d.py │ │ ├── _3d.py │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── _1d.cpython-35.pyc │ │ │ ├── _2d.cpython-35.pyc │ │ │ ├── _3d.cpython-35.pyc │ │ │ ├── __init__.cpython-35.pyc │ │ │ └── _time_distributed_2d.cpython-35.pyc │ │ └── _time_distributed_2d.py │ ├── classifiers │ │ ├── _2d.py │ │ └── __init__.py │ ├── layers │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-35.pyc │ │ │ └── _batch_normalization.cpython-35.pyc │ │ └── _batch_normalization.py │ └── models │ │ ├── _2d.py │ │ ├── __init__.py │ │ ├── __pycache__ │ │ ├── _2d.cpython-35.pyc │ │ ├── __init__.cpython-35.pyc │ │ └── _time_distributed_2d.cpython-35.pyc │ │ └── _time_distributed_2d.py └── keras_retinanet │ ├── LICENSE.txt │ ├── __init__.py │ ├── __pycache__ │ ├── __init__.cpython-35.pyc │ ├── initializers.cpython-35.pyc │ └── losses.cpython-35.pyc │ ├── backend │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-35.pyc │ │ ├── common.cpython-35.pyc │ │ ├── dynamic.cpython-35.pyc │ │ └── tensorflow_backend.cpython-35.pyc │ ├── common.py │ ├── dynamic.py │ └── tensorflow_backend.py │ ├── callbacks │ ├── __init__.py │ ├── coco.py │ ├── common.py │ └── eval.py │ ├── initializers.py │ ├── layers │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-35.pyc │ │ └── _misc.cpython-35.pyc │ └── _misc.py │ ├── losses.py │ ├── models │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-35.pyc │ │ ├── resnet.cpython-35.pyc │ │ └── retinanet.cpython-35.pyc │ ├── mobilenet.py │ ├── resnet.py │ └── retinanet.py │ ├── preprocessing │ ├── __init__.py │ ├── coco.py │ ├── csv_generator.py │ ├── generator.py │ ├── kitti.py │ ├── open_images.py │ └── pascal_voc.py │ └── utils │ ├── __init__.py │ ├── __pycache__ │ ├── __init__.cpython-35.pyc │ ├── anchors.cpython-35.pyc │ ├── colors.cpython-35.pyc │ ├── image.cpython-35.pyc │ ├── transform.cpython-35.pyc │ └── visualization.cpython-35.pyc │ ├── anchors.py │ ├── coco_eval.py │ ├── colors.py │ ├── eval.py │ ├── image.py │ ├── keras_version.py │ ├── model.py │ ├── transform.py │ └── visualization.py ├── LICENSE ├── Pothole_Detection.gif ├── Pothole_Train.py ├── Pothole_Video.py ├── README.md ├── detection_config.json └── imageai-2.0.2-py3-none-any.whlold /Detection/Custom/callbacks.py: -------------------------------------------------------------------------------- 1 | from keras.callbacks import TensorBoard, ModelCheckpoint 2 | import tensorflow as tf 3 | import numpy as np 4 | import warnings 5 | 6 | class CustomTensorBoard(TensorBoard): 7 | """ to log the loss after each batch 8 | """ 9 | def __init__(self, log_every=1, **kwargs): 10 | super(CustomTensorBoard, self).__init__(**kwargs) 11 | self.log_every = log_every 12 | self.counter = 0 13 | 14 | def on_batch_end(self, batch, logs=None): 15 | self.counter+=1 16 | if self.counter%self.log_every==0: 17 | for name, value in logs.items(): 18 | if name in ['batch', 'size']: 19 | continue 20 | summary = tf.Summary() 21 | summary_value = summary.value.add() 22 | summary_value.simple_value = value.item() 23 | summary_value.tag = name 24 | self.writer.add_summary(summary, self.counter) 25 | self.writer.flush() 26 | 27 | super(CustomTensorBoard, self).on_batch_end(batch, logs) 28 | 29 | class CustomModelCheckpoint(ModelCheckpoint): 30 | """ to save the template model, not the multi-GPU model 31 | """ 32 | def __init__(self, model_to_save, **kwargs): 33 | super(CustomModelCheckpoint, self).__init__(**kwargs) 34 | self.model_to_save = model_to_save 35 | 36 | def on_epoch_end(self, epoch, logs=None): 37 | logs = logs or {} 38 | self.epochs_since_last_save += 1 39 | if self.epochs_since_last_save >= self.period: 40 | self.epochs_since_last_save = 0 41 | filepath = self.filepath.format(epoch=epoch + 1, **logs) 42 | if self.save_best_only: 43 | current = logs.get(self.monitor) 44 | if current is None: 45 | warnings.warn('Can save best model only with %s available, ' 46 | 'skipping.' % (self.monitor), RuntimeWarning) 47 | else: 48 | if self.monitor_op(current, self.best): 49 | if self.verbose > 0: 50 | print('\nEpoch %05d: %s improved from %0.5f to %0.5f,' 51 | ' saving model to %s' 52 | % (epoch + 1, self.monitor, self.best, 53 | current, filepath)) 54 | self.best = current 55 | if self.save_weights_only: 56 | self.model_to_save.save_weights(filepath, overwrite=True) 57 | else: 58 | self.model_to_save.save(filepath, overwrite=True) 59 | else: 60 | if self.verbose > 0: 61 | print('\nEpoch %05d: %s did not improve from %0.5f' % 62 | (epoch + 1, self.monitor, self.best)) 63 | else: 64 | if self.verbose > 0: 65 | print('\nEpoch %05d: saving model to %s' % (epoch + 1, filepath)) 66 | if self.save_weights_only: 67 | self.model_to_save.save_weights(filepath, overwrite=True) 68 | else: 69 | self.model_to_save.save(filepath, overwrite=True) 70 | 71 | super(CustomModelCheckpoint, self).on_batch_end(epoch, logs) -------------------------------------------------------------------------------- /Detection/Custom/evaluate.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import argparse 4 | import os 5 | import numpy as np 6 | import json 7 | from imageai.Detection.Custom.voc import parse_voc_annotation 8 | from imageai.Detection.Custom.yolo import create_yolov3_model 9 | from imageai.Detection.Custom.generator import BatchGenerator 10 | from imageai.Detection.Custom.utils.utils import normalize, evaluate 11 | from keras.callbacks import EarlyStopping, ModelCheckpoint 12 | from keras.optimizers import Adam 13 | from keras.models import load_model 14 | 15 | def _main_(args): 16 | config_path = args.conf 17 | 18 | with open(config_path) as config_buffer: 19 | config = json.loads(config_buffer.read()) 20 | 21 | ############################### 22 | # Create the validation generator 23 | ############################### 24 | valid_ints, labels = parse_voc_annotation( 25 | config['valid']['valid_annot_folder'], 26 | config['valid']['valid_image_folder'], 27 | config['valid']['cache_name'], 28 | config['model']['labels'] 29 | ) 30 | 31 | labels = labels.keys() if len(config['model']['labels']) == 0 else config['model']['labels'] 32 | labels = sorted(labels) 33 | 34 | valid_generator = BatchGenerator( 35 | instances = valid_ints, 36 | anchors = config['model']['anchors'], 37 | labels = labels, 38 | downsample = 32, # ratio between network input's size and network output's size, 32 for YOLOv3 39 | max_box_per_image = 0, 40 | batch_size = config['train']['batch_size'], 41 | min_net_size = config['model']['min_input_size'], 42 | max_net_size = config['model']['max_input_size'], 43 | shuffle = True, 44 | jitter = 0.0, 45 | norm = normalize 46 | ) 47 | 48 | ############################### 49 | # Load the model and do evaluation 50 | ############################### 51 | os.environ['CUDA_VISIBLE_DEVICES'] = config['train']['gpus'] 52 | 53 | infer_model = load_model(config['train']['saved_weights_name']) 54 | 55 | # compute mAP for all the classes 56 | average_precisions = evaluate(infer_model, valid_generator) 57 | 58 | # print the score 59 | for label, average_precision in average_precisions.items(): 60 | print(labels[label] + ': {:.4f}'.format(average_precision)) 61 | print('mAP: {:.4f}'.format(sum(average_precisions.values()) / len(average_precisions))) 62 | 63 | if __name__ == '__main__': 64 | argparser = argparse.ArgumentParser(description='Evaluate YOLO_v3 model on any dataset') 65 | argparser.add_argument('-c', '--conf', help='path to configuration file') 66 | 67 | args = argparser.parse_args() 68 | _main_(args) 69 | -------------------------------------------------------------------------------- /Detection/Custom/gen_anchors.py: -------------------------------------------------------------------------------- 1 | import random 2 | import argparse 3 | import numpy as np 4 | 5 | from imageai.Detection.Custom.voc import parse_voc_annotation 6 | import json 7 | 8 | def IOU(ann, centroids): 9 | w, h = ann 10 | similarities = [] 11 | 12 | for centroid in centroids: 13 | c_w, c_h = centroid 14 | 15 | if c_w >= w and c_h >= h: 16 | similarity = w*h/(c_w*c_h) 17 | elif c_w >= w and c_h <= h: 18 | similarity = w*c_h/(w*h + (c_w-w)*c_h) 19 | elif c_w <= w and c_h >= h: 20 | similarity = c_w*h/(w*h + c_w*(c_h-h)) 21 | else: #means both w,h are bigger than c_w and c_h respectively 22 | similarity = (c_w*c_h)/(w*h) 23 | similarities.append(similarity) # will become (k,) shape 24 | 25 | return np.array(similarities) 26 | 27 | def avg_IOU(anns, centroids): 28 | n,d = anns.shape 29 | sum = 0. 30 | 31 | for i in range(anns.shape[0]): 32 | sum+= max(IOU(anns[i], centroids)) 33 | 34 | return sum/n 35 | 36 | 37 | def run_kmeans(ann_dims, anchor_num): 38 | ann_num = ann_dims.shape[0] 39 | iterations = 0 40 | prev_assignments = np.ones(ann_num)*(-1) 41 | iteration = 0 42 | old_distances = np.zeros((ann_num, anchor_num)) 43 | 44 | indices = [random.randrange(ann_dims.shape[0]) for i in range(anchor_num)] 45 | centroids = ann_dims[indices] 46 | anchor_dim = ann_dims.shape[1] 47 | 48 | while True: 49 | distances = [] 50 | iteration += 1 51 | for i in range(ann_num): 52 | d = 1 - IOU(ann_dims[i], centroids) 53 | distances.append(d) 54 | distances = np.array(distances) # distances.shape = (ann_num, anchor_num) 55 | 56 | #assign samples to centroids 57 | assignments = np.argmin(distances,axis=1) 58 | 59 | if (assignments == prev_assignments).all() : 60 | return centroids 61 | 62 | #calculate new centroids 63 | centroid_sums=np.zeros((anchor_num, anchor_dim), np.float) 64 | for i in range(ann_num): 65 | centroid_sums[assignments[i]]+=ann_dims[i] 66 | for j in range(anchor_num): 67 | centroids[j] = centroid_sums[j]/(np.sum(assignments==j) + 1e-6) 68 | 69 | prev_assignments = assignments.copy() 70 | old_distances = distances.copy() 71 | 72 | def generateAnchors(train_annotation_folder, train_image_folder, train_cache_file, model_labels): 73 | 74 | print("Generating anchor boxes for training images and annotation...") 75 | num_anchors = 9 76 | 77 | train_imgs, train_labels = parse_voc_annotation( 78 | train_annotation_folder, 79 | train_image_folder, 80 | train_cache_file, 81 | model_labels 82 | ) 83 | 84 | # run k_mean to find the anchors 85 | annotation_dims = [] 86 | for image in train_imgs: 87 | 88 | for obj in image['object']: 89 | relative_w = (float(obj['xmax']) - float(obj['xmin']))/image['width'] 90 | relatice_h = (float(obj["ymax"]) - float(obj['ymin']))/image['height'] 91 | annotation_dims.append(tuple(map(float, (relative_w,relatice_h)))) 92 | 93 | annotation_dims = np.array(annotation_dims) 94 | centroids = run_kmeans(annotation_dims, num_anchors) 95 | 96 | # write anchors to file 97 | print('Average IOU for', num_anchors, 'anchors:', '%0.2f' % avg_IOU(annotation_dims, centroids)) 98 | 99 | anchors = centroids.copy() 100 | 101 | widths = anchors[:, 0] 102 | sorted_indices = np.argsort(widths) 103 | 104 | 105 | anchor_array = [] 106 | reverse_anchor_array = [] 107 | out_string = "" 108 | r = "anchors: [" 109 | for i in sorted_indices: 110 | anchor_array.append(int(anchors[i, 0] * 416)) 111 | anchor_array.append(int(anchors[i, 1] * 416)) 112 | 113 | out_string += str(int(anchors[i, 0] * 416)) + ',' + str(int(anchors[i, 1] * 416)) + ', ' 114 | 115 | 116 | reverse_anchor_array.append(anchor_array[12:18]) 117 | reverse_anchor_array.append(anchor_array[6:12]) 118 | reverse_anchor_array.append(anchor_array[0:6]) 119 | 120 | print("Anchor Boxes generated.") 121 | return anchor_array, reverse_anchor_array 122 | 123 | 124 | -------------------------------------------------------------------------------- /Detection/Custom/generator.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import copy 3 | import numpy as np 4 | from keras.utils import Sequence 5 | from imageai.Detection.Custom.utils.bbox import BoundBox, bbox_iou 6 | from imageai.Detection.Custom.utils.image import apply_random_scale_and_crop, random_distort_image, random_flip, correct_bounding_boxes 7 | 8 | class BatchGenerator(Sequence): 9 | def __init__(self, 10 | instances, 11 | anchors, 12 | labels, 13 | downsample=32, # ratio between network input's size and network output's size, 32 for YOLOv3 14 | max_box_per_image=30, 15 | batch_size=1, 16 | min_net_size=320, 17 | max_net_size=608, 18 | shuffle=True, 19 | jitter=True, 20 | norm=None 21 | ): 22 | self.instances = instances 23 | self.batch_size = batch_size 24 | self.labels = labels 25 | self.downsample = downsample 26 | self.max_box_per_image = max_box_per_image 27 | self.min_net_size = (min_net_size//self.downsample)*self.downsample 28 | self.max_net_size = (max_net_size//self.downsample)*self.downsample 29 | self.shuffle = shuffle 30 | self.jitter = jitter 31 | self.norm = norm 32 | self.anchors = [BoundBox(0, 0, anchors[2*i], anchors[2*i+1]) for i in range(len(anchors)//2)] 33 | self.net_h = 416 34 | self.net_w = 416 35 | 36 | if shuffle: np.random.shuffle(self.instances) 37 | 38 | def __len__(self): 39 | return int(np.ceil(float(len(self.instances))/self.batch_size)) 40 | 41 | def __getitem__(self, idx): 42 | # get image input size, change every 10 batches 43 | net_h, net_w = self._get_net_size(idx) 44 | base_grid_h, base_grid_w = net_h//self.downsample, net_w//self.downsample 45 | 46 | # determine the first and the last indices of the batch 47 | l_bound = idx*self.batch_size 48 | r_bound = (idx+1)*self.batch_size 49 | 50 | if r_bound > len(self.instances): 51 | r_bound = len(self.instances) 52 | l_bound = r_bound - self.batch_size 53 | 54 | x_batch = np.zeros((r_bound - l_bound, net_h, net_w, 3)) # input images 55 | t_batch = np.zeros((r_bound - l_bound, 1, 1, 1, self.max_box_per_image, 4)) # list of groundtruth boxes 56 | 57 | # initialize the inputs and the outputs 58 | yolo_1 = np.zeros((r_bound - l_bound, 1*base_grid_h, 1*base_grid_w, len(self.anchors)//3, 4+1+len(self.labels))) # desired network output 1 59 | yolo_2 = np.zeros((r_bound - l_bound, 2*base_grid_h, 2*base_grid_w, len(self.anchors)//3, 4+1+len(self.labels))) # desired network output 2 60 | yolo_3 = np.zeros((r_bound - l_bound, 4*base_grid_h, 4*base_grid_w, len(self.anchors)//3, 4+1+len(self.labels))) # desired network output 3 61 | yolos = [yolo_3, yolo_2, yolo_1] 62 | 63 | dummy_yolo_1 = np.zeros((r_bound - l_bound, 1)) 64 | dummy_yolo_2 = np.zeros((r_bound - l_bound, 1)) 65 | dummy_yolo_3 = np.zeros((r_bound - l_bound, 1)) 66 | 67 | instance_count = 0 68 | true_box_index = 0 69 | 70 | # do the logic to fill in the inputs and the output 71 | for train_instance in self.instances[l_bound:r_bound]: 72 | # augment input image and fix object's position and size 73 | img, all_objs = self._aug_image(train_instance, net_h, net_w) 74 | 75 | for obj in all_objs: 76 | # find the best anchor box for this object 77 | max_anchor = None 78 | max_index = -1 79 | max_iou = -1 80 | 81 | shifted_box = BoundBox(0, 82 | 0, 83 | obj['xmax']-obj['xmin'], 84 | obj['ymax']-obj['ymin']) 85 | 86 | for i in range(len(self.anchors)): 87 | anchor = self.anchors[i] 88 | iou = bbox_iou(shifted_box, anchor) 89 | 90 | if max_iou < iou: 91 | max_anchor = anchor 92 | max_index = i 93 | max_iou = iou 94 | 95 | # determine the yolo to be responsible for this bounding box 96 | yolo = yolos[max_index//3] 97 | grid_h, grid_w = yolo.shape[1:3] 98 | 99 | # determine the position of the bounding box on the grid 100 | center_x = .5*(obj['xmin'] + obj['xmax']) 101 | center_x = center_x / float(net_w) * grid_w # sigma(t_x) + c_x 102 | center_y = .5*(obj['ymin'] + obj['ymax']) 103 | center_y = center_y / float(net_h) * grid_h # sigma(t_y) + c_y 104 | 105 | # determine the sizes of the bounding box 106 | w = np.log((obj['xmax'] - obj['xmin']) / float(max_anchor.xmax)) # t_w 107 | h = np.log((obj['ymax'] - obj['ymin']) / float(max_anchor.ymax)) # t_h 108 | 109 | box = [center_x, center_y, w, h] 110 | 111 | # determine the index of the label 112 | obj_indx = self.labels.index(obj['name']) 113 | 114 | # determine the location of the cell responsible for this object 115 | grid_x = int(np.floor(center_x)) 116 | grid_y = int(np.floor(center_y)) 117 | 118 | # assign ground truth x, y, w, h, confidence and class probs to y_batch 119 | yolo[instance_count, grid_y, grid_x, max_index%3] = 0 120 | yolo[instance_count, grid_y, grid_x, max_index%3, 0:4] = box 121 | yolo[instance_count, grid_y, grid_x, max_index%3, 4 ] = 1. 122 | yolo[instance_count, grid_y, grid_x, max_index%3, 5+obj_indx] = 1 123 | 124 | # assign the true box to t_batch 125 | true_box = [center_x, center_y, obj['xmax'] - obj['xmin'], obj['ymax'] - obj['ymin']] 126 | t_batch[instance_count, 0, 0, 0, true_box_index] = true_box 127 | 128 | true_box_index += 1 129 | true_box_index = true_box_index % self.max_box_per_image 130 | 131 | # assign input image to x_batch 132 | if self.norm != None: 133 | x_batch[instance_count] = self.norm(img) 134 | else: 135 | # plot image and bounding boxes for sanity check 136 | for obj in all_objs: 137 | cv2.rectangle(img, (obj['xmin'],obj['ymin']), (obj['xmax'],obj['ymax']), (255,0,0), 3) 138 | cv2.putText(img, obj['name'], 139 | (obj['xmin']+2, obj['ymin']+12), 140 | 0, 1.2e-3 * img.shape[0], 141 | (0,255,0), 2) 142 | 143 | x_batch[instance_count] = img 144 | 145 | # increase instance counter in the current batch 146 | instance_count += 1 147 | 148 | return [x_batch, t_batch, yolo_1, yolo_2, yolo_3], [dummy_yolo_1, dummy_yolo_2, dummy_yolo_3] 149 | 150 | def _get_net_size(self, idx): 151 | if idx%10 == 0: 152 | net_size = self.downsample*np.random.randint(self.min_net_size/self.downsample, \ 153 | self.max_net_size/self.downsample+1) 154 | 155 | self.net_h, self.net_w = net_size, net_size 156 | return self.net_h, self.net_w 157 | 158 | def _aug_image(self, instance, net_h, net_w): 159 | image_name = instance['filename'] 160 | image = cv2.imread(image_name) # RGB image 161 | 162 | if image is None: print('Cannot find ', image_name) 163 | image = image[:,:,::-1] # RGB image 164 | 165 | image_h, image_w, _ = image.shape 166 | 167 | # determine the amount of scaling and cropping 168 | dw = self.jitter * image_w; 169 | dh = self.jitter * image_h; 170 | 171 | new_ar = (image_w + np.random.uniform(-dw, dw)) / (image_h + np.random.uniform(-dh, dh)); 172 | scale = np.random.uniform(0.25, 2); 173 | 174 | if (new_ar < 1): 175 | new_h = int(scale * net_h); 176 | new_w = int(net_h * new_ar); 177 | else: 178 | new_w = int(scale * net_w); 179 | new_h = int(net_w / new_ar); 180 | 181 | dx = int(np.random.uniform(0, net_w - new_w)); 182 | dy = int(np.random.uniform(0, net_h - new_h)); 183 | 184 | # apply scaling and cropping 185 | im_sized = apply_random_scale_and_crop(image, new_w, new_h, net_w, net_h, dx, dy) 186 | 187 | # randomly distort hsv space 188 | im_sized = random_distort_image(im_sized) 189 | 190 | # randomly flip 191 | flip = np.random.randint(2) 192 | im_sized = random_flip(im_sized, flip) 193 | 194 | # correct the size and pos of bounding boxes 195 | all_objs = correct_bounding_boxes(instance['object'], new_w, new_h, net_w, net_h, dx, dy, flip, image_w, image_h) 196 | 197 | return im_sized, all_objs 198 | 199 | def on_epoch_end(self): 200 | if self.shuffle: np.random.shuffle(self.instances) 201 | 202 | def num_classes(self): 203 | return len(self.labels) 204 | 205 | def size(self): 206 | return len(self.instances) 207 | 208 | def get_anchors(self): 209 | anchors = [] 210 | 211 | for anchor in self.anchors: 212 | anchors += [anchor.xmax, anchor.ymax] 213 | 214 | return anchors 215 | 216 | def load_annotation(self, i): 217 | annots = [] 218 | 219 | for obj in self.instances[i]['object']: 220 | annot = [obj['xmin'], obj['ymin'], obj['xmax'], obj['ymax'], self.labels.index(obj['name'])] 221 | annots += [annot] 222 | 223 | if len(annots) == 0: annots = [[]] 224 | 225 | return np.array(annots) 226 | 227 | def load_image(self, i): 228 | return cv2.imread(self.instances[i]['filename']) -------------------------------------------------------------------------------- /Detection/Custom/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/Custom/utils/__init__.py -------------------------------------------------------------------------------- /Detection/Custom/utils/bbox.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | import cv2 4 | from .colors import get_color 5 | 6 | class BoundBox: 7 | def __init__(self, xmin, ymin, xmax, ymax, c = None, classes = None): 8 | self.xmin = xmin 9 | self.ymin = ymin 10 | self.xmax = xmax 11 | self.ymax = ymax 12 | 13 | self.c = c 14 | self.classes = classes 15 | 16 | self.label = -1 17 | self.score = -1 18 | 19 | def get_label(self): 20 | if self.label == -1: 21 | self.label = np.argmax(self.classes) 22 | 23 | return self.label 24 | 25 | def get_score(self): 26 | if self.score == -1: 27 | self.score = self.classes[self.get_label()] 28 | 29 | return self.score 30 | 31 | def _interval_overlap(interval_a, interval_b): 32 | x1, x2 = interval_a 33 | x3, x4 = interval_b 34 | 35 | if x3 < x1: 36 | if x4 < x1: 37 | return 0 38 | else: 39 | return min(x2,x4) - x1 40 | else: 41 | if x2 < x3: 42 | return 0 43 | else: 44 | return min(x2,x4) - x3 45 | 46 | def bbox_iou(box1, box2): 47 | intersect_w = _interval_overlap([box1.xmin, box1.xmax], [box2.xmin, box2.xmax]) 48 | intersect_h = _interval_overlap([box1.ymin, box1.ymax], [box2.ymin, box2.ymax]) 49 | 50 | intersect = intersect_w * intersect_h 51 | 52 | w1, h1 = box1.xmax-box1.xmin, box1.ymax-box1.ymin 53 | w2, h2 = box2.xmax-box2.xmin, box2.ymax-box2.ymin 54 | 55 | union = w1*h1 + w2*h2 - intersect 56 | 57 | if(union <= 0): 58 | union = 1 59 | 60 | return float(intersect) / float(union) 61 | 62 | def draw_boxes(image, boxes, labels, obj_thresh, quiet=True): 63 | for box in boxes: 64 | label_str = '' 65 | label = -1 66 | 67 | for i in range(len(labels)): 68 | if box.classes[i] > obj_thresh: 69 | if label_str != '': label_str += ', ' 70 | label_str += (labels[i] + ' ' + str(round(box.get_score()*100, 2)) + '%') 71 | label = i 72 | if not quiet: print(label_str) 73 | 74 | if label >= 0: 75 | text_size = cv2.getTextSize(label_str, cv2.FONT_HERSHEY_SIMPLEX, 1.1e-3 * image.shape[0], 5) 76 | width, height = text_size[0][0], text_size[0][1] 77 | region = np.array([[box.xmin-3, box.ymin], 78 | [box.xmin-3, box.ymin-height-26], 79 | [box.xmin+width+13, box.ymin-height-26], 80 | [box.xmin+width+13, box.ymin]], dtype='int32') 81 | 82 | cv2.rectangle(img=image, pt1=(box.xmin,box.ymin), pt2=(box.xmax,box.ymax), color=get_color(label), thickness=5) 83 | cv2.fillPoly(img=image, pts=[region], color=get_color(label)) 84 | cv2.putText(img=image, 85 | text=label_str, 86 | org=(box.xmin+13, box.ymin - 13), 87 | fontFace=cv2.FONT_HERSHEY_SIMPLEX, 88 | fontScale=1e-3 * image.shape[0], 89 | color=(0,0,0), 90 | thickness=2) 91 | 92 | return image -------------------------------------------------------------------------------- /Detection/Custom/utils/colors.py: -------------------------------------------------------------------------------- 1 | def get_color(label): 2 | """ Return a color from a set of predefined colors. Contains 80 colors in total. 3 | code originally from https://github.com/fizyr/keras-retinanet/ 4 | Args 5 | label: The label to get the color for. 6 | Returns 7 | A list of three values representing a RGB color. 8 | """ 9 | if label < len(colors): 10 | return colors[label] 11 | else: 12 | print('Label {} has no color, returning default.'.format(label)) 13 | return (0, 255, 0) 14 | 15 | colors = [ 16 | [31 , 0 , 255] , 17 | [0 , 159 , 255] , 18 | [255 , 95 , 0] , 19 | [255 , 19 , 0] , 20 | [255 , 0 , 0] , 21 | [255 , 38 , 0] , 22 | [0 , 255 , 25] , 23 | [255 , 0 , 133] , 24 | [255 , 172 , 0] , 25 | [108 , 0 , 255] , 26 | [0 , 82 , 255] , 27 | [0 , 255 , 6] , 28 | [255 , 0 , 152] , 29 | [223 , 0 , 255] , 30 | [12 , 0 , 255] , 31 | [0 , 255 , 178] , 32 | [108 , 255 , 0] , 33 | [184 , 0 , 255] , 34 | [255 , 0 , 76] , 35 | [146 , 255 , 0] , 36 | [51 , 0 , 255] , 37 | [0 , 197 , 255] , 38 | [255 , 248 , 0] , 39 | [255 , 0 , 19] , 40 | [255 , 0 , 38] , 41 | [89 , 255 , 0] , 42 | [127 , 255 , 0] , 43 | [255 , 153 , 0] , 44 | [0 , 255 , 255] , 45 | [0 , 255 , 216] , 46 | [0 , 255 , 121] , 47 | [255 , 0 , 248] , 48 | [70 , 0 , 255] , 49 | [0 , 255 , 159] , 50 | [0 , 216 , 255] , 51 | [0 , 6 , 255] , 52 | [0 , 63 , 255] , 53 | [31 , 255 , 0] , 54 | [255 , 57 , 0] , 55 | [255 , 0 , 210] , 56 | [0 , 255 , 102] , 57 | [242 , 255 , 0] , 58 | [255 , 191 , 0] , 59 | [0 , 255 , 63] , 60 | [255 , 0 , 95] , 61 | [146 , 0 , 255] , 62 | [184 , 255 , 0] , 63 | [255 , 114 , 0] , 64 | [0 , 255 , 235] , 65 | [255 , 229 , 0] , 66 | [0 , 178 , 255] , 67 | [255 , 0 , 114] , 68 | [255 , 0 , 57] , 69 | [0 , 140 , 255] , 70 | [0 , 121 , 255] , 71 | [12 , 255 , 0] , 72 | [255 , 210 , 0] , 73 | [0 , 255 , 44] , 74 | [165 , 255 , 0] , 75 | [0 , 25 , 255] , 76 | [0 , 255 , 140] , 77 | [0 , 101 , 255] , 78 | [0 , 255 , 82] , 79 | [223 , 255 , 0] , 80 | [242 , 0 , 255] , 81 | [89 , 0 , 255] , 82 | [165 , 0 , 255] , 83 | [70 , 255 , 0] , 84 | [255 , 0 , 172] , 85 | [255 , 76 , 0] , 86 | [203 , 255 , 0] , 87 | [204 , 0 , 255] , 88 | [255 , 0 , 229] , 89 | [255 , 133 , 0] , 90 | [127 , 0 , 255] , 91 | [0 , 235 , 255] , 92 | [0 , 255 , 197] , 93 | [255 , 0 , 191] , 94 | [0 , 44 , 255] , 95 | [50 , 255 , 0] 96 | ] 97 | -------------------------------------------------------------------------------- /Detection/Custom/utils/image.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | import copy 4 | 5 | def _rand_scale(scale): 6 | scale = np.random.uniform(1, scale) 7 | return scale if (np.random.randint(2) == 0) else 1./scale; 8 | 9 | def _constrain(min_v, max_v, value): 10 | if value < min_v: return min_v 11 | if value > max_v: return max_v 12 | return value 13 | 14 | def random_flip(image, flip): 15 | if flip == 1: return cv2.flip(image, 1) 16 | return image 17 | 18 | def correct_bounding_boxes(boxes, new_w, new_h, net_w, net_h, dx, dy, flip, image_w, image_h): 19 | boxes = copy.deepcopy(boxes) 20 | 21 | # randomize boxes' order 22 | np.random.shuffle(boxes) 23 | 24 | # correct sizes and positions 25 | sx, sy = float(new_w)/image_w, float(new_h)/image_h 26 | zero_boxes = [] 27 | 28 | for i in range(len(boxes)): 29 | boxes[i]['xmin'] = int(_constrain(0, net_w, boxes[i]['xmin']*sx + dx)) 30 | boxes[i]['xmax'] = int(_constrain(0, net_w, boxes[i]['xmax']*sx + dx)) 31 | boxes[i]['ymin'] = int(_constrain(0, net_h, boxes[i]['ymin']*sy + dy)) 32 | boxes[i]['ymax'] = int(_constrain(0, net_h, boxes[i]['ymax']*sy + dy)) 33 | 34 | if boxes[i]['xmax'] <= boxes[i]['xmin'] or boxes[i]['ymax'] <= boxes[i]['ymin']: 35 | zero_boxes += [i] 36 | continue 37 | 38 | if flip == 1: 39 | swap = boxes[i]['xmin']; 40 | boxes[i]['xmin'] = net_w - boxes[i]['xmax'] 41 | boxes[i]['xmax'] = net_w - swap 42 | 43 | boxes = [boxes[i] for i in range(len(boxes)) if i not in zero_boxes] 44 | 45 | return boxes 46 | 47 | def random_distort_image(image, hue=18, saturation=1.5, exposure=1.5): 48 | # determine scale factors 49 | dhue = np.random.uniform(-hue, hue) 50 | dsat = _rand_scale(saturation); 51 | dexp = _rand_scale(exposure); 52 | 53 | # convert RGB space to HSV space 54 | image = cv2.cvtColor(image, cv2.COLOR_RGB2HSV).astype('float') 55 | 56 | # change satuation and exposure 57 | image[:,:,1] *= dsat 58 | image[:,:,2] *= dexp 59 | 60 | # change hue 61 | image[:,:,0] += dhue 62 | image[:,:,0] -= (image[:,:,0] > 180)*180 63 | image[:,:,0] += (image[:,:,0] < 0) *180 64 | 65 | # convert back to RGB from HSV 66 | return cv2.cvtColor(image.astype('uint8'), cv2.COLOR_HSV2RGB) 67 | 68 | def apply_random_scale_and_crop(image, new_w, new_h, net_w, net_h, dx, dy): 69 | im_sized = cv2.resize(image, (new_w, new_h)) 70 | 71 | if dx > 0: 72 | im_sized = np.pad(im_sized, ((0,0), (dx,0), (0,0)), mode='constant', constant_values=127) 73 | else: 74 | im_sized = im_sized[:,-dx:,:] 75 | if (new_w + dx) < net_w: 76 | im_sized = np.pad(im_sized, ((0,0), (0, net_w - (new_w+dx)), (0,0)), mode='constant', constant_values=127) 77 | 78 | if dy > 0: 79 | im_sized = np.pad(im_sized, ((dy,0), (0,0), (0,0)), mode='constant', constant_values=127) 80 | else: 81 | im_sized = im_sized[-dy:,:,:] 82 | 83 | if (new_h + dy) < net_h: 84 | im_sized = np.pad(im_sized, ((0, net_h - (new_h+dy)), (0,0), (0,0)), mode='constant', constant_values=127) 85 | 86 | return im_sized[:net_h, :net_w,:] -------------------------------------------------------------------------------- /Detection/Custom/utils/multi_gpu_model.py: -------------------------------------------------------------------------------- 1 | from keras.layers import Lambda, concatenate 2 | from keras.models import Model 3 | import tensorflow as tf 4 | 5 | def multi_gpu_model(model, gpus): 6 | if isinstance(gpus, (list, tuple)): 7 | num_gpus = len(gpus) 8 | target_gpu_ids = gpus 9 | else: 10 | num_gpus = gpus 11 | target_gpu_ids = range(num_gpus) 12 | 13 | def get_slice(data, i, parts): 14 | shape = tf.shape(data) 15 | batch_size = shape[:1] 16 | input_shape = shape[1:] 17 | step = batch_size // parts 18 | if i == num_gpus - 1: 19 | size = batch_size - step * i 20 | else: 21 | size = step 22 | size = tf.concat([size, input_shape], axis=0) 23 | stride = tf.concat([step, input_shape * 0], axis=0) 24 | start = stride * i 25 | return tf.slice(data, start, size) 26 | 27 | all_outputs = [] 28 | for i in range(len(model.outputs)): 29 | all_outputs.append([]) 30 | 31 | # Place a copy of the model on each GPU, 32 | # each getting a slice of the inputs. 33 | for i, gpu_id in enumerate(target_gpu_ids): 34 | with tf.device('/gpu:%d' % gpu_id): 35 | with tf.name_scope('replica_%d' % gpu_id): 36 | inputs = [] 37 | # Retrieve a slice of the input. 38 | for x in model.inputs: 39 | input_shape = tuple(x.get_shape().as_list())[1:] 40 | slice_i = Lambda(get_slice, 41 | output_shape=input_shape, 42 | arguments={'i': i, 43 | 'parts': num_gpus})(x) 44 | inputs.append(slice_i) 45 | 46 | # Apply model on slice 47 | # (creating a model replica on the target device). 48 | outputs = model(inputs) 49 | if not isinstance(outputs, list): 50 | outputs = [outputs] 51 | 52 | # Save the outputs for merging back together later. 53 | for o in range(len(outputs)): 54 | all_outputs[o].append(outputs[o]) 55 | 56 | # Merge outputs on CPU. 57 | with tf.device('/cpu:0'): 58 | merged = [] 59 | for name, outputs in zip(model.output_names, all_outputs): 60 | merged.append(concatenate(outputs, 61 | axis=0, name=name)) 62 | return Model(model.inputs, merged) -------------------------------------------------------------------------------- /Detection/Custom/voc.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | import xml.etree.ElementTree as ET 4 | import pickle 5 | 6 | def parse_voc_annotation(ann_dir, img_dir, cache_name, labels=[]): 7 | if os.path.exists(cache_name): 8 | with open(cache_name, 'rb') as handle: 9 | cache = pickle.load(handle) 10 | all_insts, seen_labels = cache['all_insts'], cache['seen_labels'] 11 | else: 12 | all_insts = [] 13 | seen_labels = {} 14 | 15 | for ann in sorted(os.listdir(ann_dir)): 16 | img = {'object':[]} 17 | 18 | try: 19 | tree = ET.parse(ann_dir + ann) 20 | except Exception as e: 21 | print(e) 22 | print('Ignore this bad annotation: ' + ann_dir + ann) 23 | continue 24 | 25 | for elem in tree.iter(): 26 | if 'filename' in elem.tag: 27 | img['filename'] = img_dir + elem.text 28 | if 'width' in elem.tag: 29 | img['width'] = int(elem.text) 30 | if 'height' in elem.tag: 31 | img['height'] = int(elem.text) 32 | if 'object' in elem.tag or 'part' in elem.tag: 33 | obj = {} 34 | 35 | for attr in list(elem): 36 | if 'name' in attr.tag: 37 | obj['name'] = attr.text 38 | 39 | if obj['name'] in seen_labels: 40 | seen_labels[obj['name']] += 1 41 | else: 42 | seen_labels[obj['name']] = 1 43 | 44 | if len(labels) > 0 and obj['name'] not in labels: 45 | break 46 | else: 47 | img['object'] += [obj] 48 | 49 | if 'bndbox' in attr.tag: 50 | for dim in list(attr): 51 | if 'xmin' in dim.tag: 52 | obj['xmin'] = int(round(float(dim.text))) 53 | if 'ymin' in dim.tag: 54 | obj['ymin'] = int(round(float(dim.text))) 55 | if 'xmax' in dim.tag: 56 | obj['xmax'] = int(round(float(dim.text))) 57 | if 'ymax' in dim.tag: 58 | obj['ymax'] = int(round(float(dim.text))) 59 | 60 | if len(img['object']) > 0: 61 | all_insts += [img] 62 | 63 | cache = {'all_insts': all_insts, 'seen_labels': seen_labels} 64 | with open(cache_name, 'wb') as handle: 65 | pickle.dump(cache, handle, protocol=pickle.HIGHEST_PROTOCOL) 66 | 67 | return all_insts, seen_labels -------------------------------------------------------------------------------- /Detection/YOLOv3/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/YOLOv3/__init__.py -------------------------------------------------------------------------------- /Detection/YOLOv3/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/YOLOv3/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/YOLOv3/__pycache__/models.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/YOLOv3/__pycache__/models.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/YOLOv3/__pycache__/utils.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/YOLOv3/__pycache__/utils.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/YOLOv3/models.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | 3 | import numpy as np 4 | import tensorflow as tf 5 | from keras import backend as K 6 | from keras.layers import Conv2D, MaxPool2D, Add, ZeroPadding2D, UpSampling2D, Concatenate 7 | from keras.layers.advanced_activations import LeakyReLU 8 | from keras.layers.normalization import BatchNormalization 9 | from keras.regularizers import l2 10 | from keras.models import Model, Input 11 | 12 | 13 | 14 | def NetworkConv2D_BN_Leaky(input, channels, kernel_size, kernel_regularizer = l2(5e-4), strides=(1,1), padding="same", use_bias=False): 15 | 16 | network = Conv2D( filters=channels, kernel_size=kernel_size, strides=strides, padding=padding, kernel_regularizer=kernel_regularizer, use_bias=use_bias)(input) 17 | network = BatchNormalization()(network) 18 | network = LeakyReLU(alpha=0.1)(network) 19 | return network 20 | 21 | def residual_block(input, channels, num_blocks): 22 | network = ZeroPadding2D(((1,0), (1,0)))(input) 23 | network = NetworkConv2D_BN_Leaky(input=network,channels=channels, kernel_size=(3,3), strides=(2,2), padding="valid") 24 | 25 | for blocks in range(num_blocks): 26 | network_1 = NetworkConv2D_BN_Leaky(input=network, channels= channels // 2, kernel_size=(1,1)) 27 | network_1 = NetworkConv2D_BN_Leaky(input=network_1,channels= channels, kernel_size=(3,3)) 28 | 29 | network = Add()([network, network_1]) 30 | return network 31 | 32 | def darknet(input): 33 | network = NetworkConv2D_BN_Leaky(input=input, channels=32, kernel_size=(3,3)) 34 | network = residual_block(input=network, channels=64, num_blocks=1) 35 | network = residual_block(input=network, channels=128, num_blocks=2) 36 | network = residual_block(input=network, channels=256, num_blocks=8) 37 | network = residual_block(input=network, channels=512, num_blocks=8) 38 | network = residual_block(input=network, channels=1024, num_blocks=4) 39 | 40 | 41 | return network 42 | 43 | def last_layers(input, channels_in, channels_out, layer_name=""): 44 | 45 | 46 | 47 | network = NetworkConv2D_BN_Leaky( input=input, channels=channels_in, kernel_size=(1,1)) 48 | network = NetworkConv2D_BN_Leaky(input=network, channels= (channels_in * 2) , kernel_size=(3, 3)) 49 | network = NetworkConv2D_BN_Leaky(input=network, channels=channels_in, kernel_size=(1, 1)) 50 | network = NetworkConv2D_BN_Leaky(input=network, channels=(channels_in * 2), kernel_size=(3, 3)) 51 | network = NetworkConv2D_BN_Leaky(input=network, channels=channels_in, kernel_size=(1, 1)) 52 | 53 | network_1 = NetworkConv2D_BN_Leaky(input=network, channels=(channels_in * 2), kernel_size=(3, 3)) 54 | network_1 = Conv2D(filters=channels_out, kernel_size=(1,1), name=layer_name)(network_1) 55 | 56 | return network, network_1 57 | 58 | def yolo_main(input, num_anchors, num_classes): 59 | 60 | darknet_network = Model(input, darknet(input)) 61 | 62 | network, network_1 = last_layers(darknet_network.output, 512, num_anchors * (num_classes + 5), layer_name="last1") 63 | 64 | network = NetworkConv2D_BN_Leaky( input=network, channels=256, kernel_size=(1,1)) 65 | network = UpSampling2D(2)(network) 66 | network = Concatenate()([network, darknet_network.layers[152].output]) 67 | 68 | network, network_2 = last_layers(network, 256, num_anchors * (num_classes + 5), layer_name="last2") 69 | 70 | network = NetworkConv2D_BN_Leaky(input=network, channels=128, kernel_size=(1, 1)) 71 | network = UpSampling2D(2)(network) 72 | network = Concatenate()([network, darknet_network.layers[92].output]) 73 | 74 | network, network_3 = last_layers(network, 128, num_anchors * (num_classes + 5), layer_name="last3") 75 | 76 | return Model(input, [network_1, network_2, network_3]) 77 | 78 | 79 | def tiny_yolo_main(input, num_anchors, num_classes): 80 | network_1 = NetworkConv2D_BN_Leaky(input=input, channels=16, kernel_size=(3,3) ) 81 | network_1 = MaxPool2D(pool_size=(2,2), strides=(2,2), padding="same")(network_1) 82 | network_1 = NetworkConv2D_BN_Leaky(input=network_1, channels=32, kernel_size=(3, 3)) 83 | network_1 = MaxPool2D(pool_size=(2, 2), strides=(2, 2), padding="same")(network_1) 84 | network_1 = NetworkConv2D_BN_Leaky(input=network_1, channels=64, kernel_size=(3, 3)) 85 | network_1 = MaxPool2D(pool_size=(2, 2), strides=(2, 2), padding="same")(network_1) 86 | network_1 = NetworkConv2D_BN_Leaky(input=network_1, channels=128, kernel_size=(3, 3)) 87 | network_1 = MaxPool2D(pool_size=(2, 2), strides=(2, 2), padding="same")(network_1) 88 | network_1 = NetworkConv2D_BN_Leaky(input=network_1, channels=256, kernel_size=(3, 3)) 89 | 90 | network_2 = MaxPool2D(pool_size=(2, 2), strides=(2, 2), padding="same")(network_1) 91 | network_2 = NetworkConv2D_BN_Leaky(input=network_2, channels=512, kernel_size=(3, 3)) 92 | network_2 = MaxPool2D(pool_size=(2, 2), strides=(1, 1), padding="same")(network_2) 93 | network_2 = NetworkConv2D_BN_Leaky(input=network_2, channels=1024, kernel_size=(3, 3)) 94 | network_2 = NetworkConv2D_BN_Leaky(input=network_2, channels=256, kernel_size=(1, 1)) 95 | 96 | network_3 = NetworkConv2D_BN_Leaky(input=network_2, channels=512, kernel_size=(3, 3)) 97 | network_3 = Conv2D(num_anchors * (num_classes + 5), kernel_size=(1,1))(network_3) 98 | 99 | network_2 = NetworkConv2D_BN_Leaky(input=network_2, channels=128, kernel_size=(1, 1)) 100 | network_2 = UpSampling2D(2)(network_2) 101 | 102 | network_4 = Concatenate()([network_2, network_1]) 103 | network_4 = NetworkConv2D_BN_Leaky(input=network_4, channels=256, kernel_size=(3, 3)) 104 | network_4 = Conv2D(num_anchors * (num_classes + 5), kernel_size=(1,1))(network_4) 105 | 106 | return Model(input, [network_3, network_4]) -------------------------------------------------------------------------------- /Detection/YOLOv3/utils.py: -------------------------------------------------------------------------------- 1 | import tensorflow as tf 2 | import tensorflow as tf 3 | from keras import backend as K 4 | 5 | from PIL import Image 6 | 7 | 8 | def yolo_head(feats, anchors, num_classes, input_shape, calc_loss=False): 9 | 10 | num_anchors = len(anchors) 11 | 12 | anchors_tensor = K.reshape(K.constant(anchors), [1, 1, 1, num_anchors, 2]) 13 | 14 | grid_shape = K.shape(feats)[1:3] 15 | grid_y = K.tile(K.reshape(K.arange(0, stop=grid_shape[0]), [-1, 1, 1, 1]), 16 | [1, grid_shape[1], 1, 1]) 17 | grid_x = K.tile(K.reshape(K.arange(0, stop=grid_shape[1]), [1, -1, 1, 1]), 18 | [grid_shape[0], 1, 1, 1]) 19 | grid = K.concatenate([grid_x, grid_y]) 20 | grid = K.cast(grid, K.dtype(feats)) 21 | 22 | feats = K.reshape( 23 | feats, [-1, grid_shape[0], grid_shape[1], num_anchors, num_classes + 5]) 24 | 25 | 26 | box_xy = (K.sigmoid(feats[..., :2]) + grid) / K.cast(grid_shape[::-1], K.dtype(feats)) 27 | box_wh = K.exp(feats[..., 2:4]) * anchors_tensor / K.cast(input_shape[::-1], K.dtype(feats)) 28 | box_confidence = K.sigmoid(feats[..., 4:5]) 29 | box_class_probs = K.sigmoid(feats[..., 5:]) 30 | 31 | if calc_loss == True: 32 | return grid, feats, box_xy, box_wh 33 | return box_xy, box_wh, box_confidence, box_class_probs 34 | 35 | 36 | def yolo_correct_boxes(box_xy, box_wh, input_shape, image_shape): 37 | 38 | box_yx = box_xy[..., ::-1] 39 | box_hw = box_wh[..., ::-1] 40 | input_shape = K.cast(input_shape, K.dtype(box_yx)) 41 | image_shape = K.cast(image_shape, K.dtype(box_yx)) 42 | new_shape = K.round(image_shape * K.min(input_shape/image_shape)) 43 | offset = (input_shape-new_shape)/2./input_shape 44 | scale = input_shape/new_shape 45 | box_yx = (box_yx - offset) * scale 46 | box_hw *= scale 47 | 48 | box_mins = box_yx - (box_hw / 2.) 49 | box_maxes = box_yx + (box_hw / 2.) 50 | boxes = K.concatenate([ 51 | box_mins[..., 0:1], 52 | box_mins[..., 1:2], 53 | box_maxes[..., 0:1], 54 | box_maxes[..., 1:2] 55 | ]) 56 | 57 | 58 | boxes *= K.concatenate([image_shape, image_shape]) 59 | return boxes 60 | 61 | 62 | def yolo_boxes_and_scores(feats, anchors, num_classes, input_shape, image_shape): 63 | 64 | box_xy, box_wh, box_confidence, box_class_probs = yolo_head(feats, 65 | anchors, num_classes, input_shape) 66 | boxes = yolo_correct_boxes(box_xy, box_wh, input_shape, image_shape) 67 | boxes = K.reshape(boxes, [-1, 4]) 68 | box_scores = box_confidence * box_class_probs 69 | box_scores = K.reshape(box_scores, [-1, num_classes]) 70 | return boxes, box_scores 71 | 72 | 73 | def yolo_eval(yolo_outputs, 74 | anchors, 75 | num_classes, 76 | image_shape, 77 | max_boxes=20, 78 | score_threshold=.6, 79 | iou_threshold=.5): 80 | 81 | num_layers = len(yolo_outputs) 82 | anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] if num_layers==3 else [[3,4,5], [1,2,3]] 83 | input_shape = K.shape(yolo_outputs[0])[1:3] * 32 84 | boxes = [] 85 | box_scores = [] 86 | for l in range(num_layers): 87 | _boxes, _box_scores = yolo_boxes_and_scores(yolo_outputs[l], 88 | anchors[anchor_mask[l]], num_classes, input_shape, image_shape) 89 | boxes.append(_boxes) 90 | box_scores.append(_box_scores) 91 | boxes = K.concatenate(boxes, axis=0) 92 | box_scores = K.concatenate(box_scores, axis=0) 93 | 94 | mask = box_scores >= score_threshold 95 | max_boxes_tensor = K.constant(max_boxes, dtype='int32') 96 | boxes_ = [] 97 | scores_ = [] 98 | classes_ = [] 99 | for c in range(num_classes): 100 | class_boxes = tf.boolean_mask(boxes, mask[:, c]) 101 | class_box_scores = tf.boolean_mask(box_scores[:, c], mask[:, c]) 102 | nms_index = tf.image.non_max_suppression( 103 | class_boxes, class_box_scores, max_boxes_tensor, iou_threshold=iou_threshold) 104 | class_boxes = K.gather(class_boxes, nms_index) 105 | class_box_scores = K.gather(class_box_scores, nms_index) 106 | classes = K.ones_like(class_box_scores, 'int32') * c 107 | boxes_.append(class_boxes) 108 | scores_.append(class_box_scores) 109 | classes_.append(classes) 110 | boxes_ = K.concatenate(boxes_, axis=0) 111 | scores_ = K.concatenate(scores_, axis=0) 112 | classes_ = K.concatenate(classes_, axis=0) 113 | 114 | return boxes_, scores_, classes_ 115 | 116 | 117 | 118 | def letterbox_image(image, size): 119 | iw, ih = image.size 120 | w, h = size 121 | scale = min(w/iw, h/ih) 122 | nw = int(iw*scale) 123 | nh = int(ih*scale) 124 | 125 | image = image.resize((nw,nh), Image.BICUBIC) 126 | new_image = Image.new('RGB', size, (128,128,128)) 127 | new_image.paste(image, ((w-nw)//2, (h-nh)//2)) 128 | return new_image -------------------------------------------------------------------------------- /Detection/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_resnet/__init__.py: -------------------------------------------------------------------------------- 1 | from . import layers 2 | 3 | custom_objects = { 4 | 'BatchNormalization': layers.BatchNormalization, 5 | } 6 | -------------------------------------------------------------------------------- /Detection/keras_resnet/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_resnet/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_resnet/benchmarks/__init__.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | import click 4 | import keras 5 | import keras.preprocessing.image 6 | import numpy 7 | import pkg_resources 8 | import sklearn.model_selection 9 | import tensorflow 10 | 11 | import keras_resnet.classifiers 12 | 13 | _benchmarks = { 14 | "CIFAR-10": keras.datasets.cifar10, 15 | "CIFAR-100": keras.datasets.cifar100, 16 | "MNIST": keras.datasets.mnist 17 | } 18 | 19 | 20 | _names = { 21 | "ResNet-18": keras_resnet.classifiers.ResNet18, 22 | "ResNet-34": keras_resnet.classifiers.ResNet34, 23 | "ResNet-50": keras_resnet.classifiers.ResNet50, 24 | "ResNet-101": keras_resnet.classifiers.ResNet101, 25 | "ResNet-152": keras_resnet.classifiers.ResNet152, 26 | "ResNet-200": keras_resnet.classifiers.ResNet200 27 | } 28 | 29 | 30 | @click.command() 31 | @click.option( 32 | "--benchmark", 33 | default="CIFAR-10", 34 | type=click.Choice( 35 | [ 36 | "CIFAR-10", 37 | "CIFAR-100", 38 | "ImageNet", 39 | "MNIST" 40 | ] 41 | ) 42 | ) 43 | @click.option("--device", default=0) 44 | @click.option( 45 | "--name", 46 | default="ResNet-50", 47 | type=click.Choice( 48 | [ 49 | "ResNet-18", 50 | "ResNet-34", 51 | "ResNet-50", 52 | "ResNet-101", 53 | "ResNet-152", 54 | "ResNet-200" 55 | ] 56 | ) 57 | ) 58 | def __main__(benchmark, device, name): 59 | configuration = tensorflow.ConfigProto() 60 | 61 | configuration.gpu_options.allow_growth = True 62 | 63 | configuration.gpu_options.visible_device_list = str(device) 64 | 65 | session = tensorflow.Session(config=configuration) 66 | 67 | keras.backend.set_session(session) 68 | 69 | (training_x, training_y), _ = _benchmarks[benchmark].load_data() 70 | 71 | training_x = training_x.astype(numpy.float16) 72 | 73 | training_y = keras.utils.np_utils.to_categorical(training_y) 74 | 75 | training_x, validation_x, training_y, validation_y = sklearn.model_selection.train_test_split(training_x, training_y) 76 | 77 | generator = keras.preprocessing.image.ImageDataGenerator( 78 | horizontal_flip=True 79 | ) 80 | 81 | generator.fit(training_x) 82 | 83 | generator = generator.flow( 84 | x=training_x, 85 | y=training_y, 86 | batch_size=256 87 | ) 88 | 89 | validation_data = keras.preprocessing.image.ImageDataGenerator() 90 | 91 | validation_data.fit(validation_x) 92 | 93 | validation_data = validation_data.flow( 94 | x=validation_x, 95 | y=validation_y, 96 | batch_size=256 97 | ) 98 | 99 | shape, classes = training_x.shape[1:], training_y.shape[-1] 100 | 101 | x = keras.layers.Input(shape) 102 | 103 | model = _names[name](x, classes) 104 | 105 | model.compile("adam", "categorical_crossentropy", ["accuracy"]) 106 | 107 | pathname = os.path.join("data", "checkpoints", benchmark, "{}.hdf5".format(name)) 108 | 109 | pathname = pkg_resources.resource_filename("keras_resnet", pathname) 110 | 111 | model_checkpoint = keras.callbacks.ModelCheckpoint(pathname) 112 | 113 | pathname = os.path.join("data", "logs", benchmark, "{}.csv".format(name)) 114 | 115 | pathname = pkg_resources.resource_filename("keras_resnet", pathname) 116 | 117 | csv_logger = keras.callbacks.CSVLogger(pathname) 118 | 119 | callbacks = [ 120 | csv_logger, 121 | model_checkpoint 122 | ] 123 | 124 | model.fit_generator( 125 | callbacks=callbacks, 126 | epochs=200, 127 | generator=generator, 128 | steps_per_epoch=training_x.shape[0] // 256, 129 | validation_data=validation_data, 130 | validation_steps=validation_x.shape[0] // 256 131 | ) 132 | 133 | if __name__ == "__main__": 134 | __main__() 135 | -------------------------------------------------------------------------------- /Detection/keras_resnet/blocks/_1d.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | keras_resnet.blocks._1d 5 | ~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module implements a number of popular one-dimensional residual blocks. 8 | """ 9 | 10 | import keras.layers 11 | import keras.regularizers 12 | from imageai.Detection import keras_resnet 13 | from imageai.Detection.keras_resnet import layers 14 | 15 | parameters = { 16 | "kernel_initializer": "he_normal" 17 | } 18 | 19 | 20 | def basic_1d(filters, stage=0, block=0, kernel_size=3, numerical_name=False, stride=None, freeze_bn=False): 21 | """ 22 | A one-dimensional basic block. 23 | 24 | :param filters: the output’s feature space 25 | 26 | :param stage: int representing the stage of this block (starting from 0) 27 | 28 | :param block: int representing this block (starting from 0) 29 | 30 | :param kernel_size: size of the kernel 31 | 32 | :param numerical_name: if true, uses numbers to represent blocks instead of chars (ResNet{101, 152, 200}) 33 | 34 | :param stride: int representing the stride used in the shortcut and the first conv layer, default derives stride from block id 35 | 36 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 37 | 38 | Usage: 39 | 40 | >>> import keras_resnet.blocks 41 | 42 | >>> keras_resnet.blocks.basic_1d(64) 43 | """ 44 | if stride is None: 45 | if block != 0 or stage == 0: 46 | stride = 1 47 | else: 48 | stride = 2 49 | 50 | if keras.backend.image_data_format() == "channels_last": 51 | axis = 3 52 | else: 53 | axis = 1 54 | 55 | if block > 0 and numerical_name: 56 | block_char = "b{}".format(block) 57 | else: 58 | block_char = chr(ord('a') + block) 59 | 60 | stage_char = str(stage + 2) 61 | 62 | def f(x): 63 | y = keras.layers.ZeroPadding1D(padding=1, name="padding{}{}_branch2a".format(stage_char, block_char))(x) 64 | y = keras.layers.Conv1D(filters, kernel_size, strides=stride, use_bias=False, name="res{}{}_branch2a".format(stage_char, block_char), **parameters)(y) 65 | y = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2a".format(stage_char, block_char))(y) 66 | y = keras.layers.Activation("relu", name="res{}{}_branch2a_relu".format(stage_char, block_char))(y) 67 | 68 | y = keras.layers.ZeroPadding1D(padding=1, name="padding{}{}_branch2b".format(stage_char, block_char))(y) 69 | y = keras.layers.Conv1D(filters, kernel_size, use_bias=False, name="res{}{}_branch2b".format(stage_char, block_char), **parameters)(y) 70 | y = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2b".format(stage_char, block_char))(y) 71 | 72 | if block == 0: 73 | shortcut = keras.layers.Conv1D(filters, (1, 1), strides=stride, use_bias=False, name="res{}{}_branch1".format(stage_char, block_char), **parameters)(x) 74 | shortcut = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch1".format(stage_char, block_char))(shortcut) 75 | else: 76 | shortcut = x 77 | 78 | y = keras.layers.Add(name="res{}{}".format(stage_char, block_char))([y, shortcut]) 79 | y = keras.layers.Activation("relu", name="res{}{}_relu".format(stage_char, block_char))(y) 80 | 81 | return y 82 | 83 | return f 84 | 85 | 86 | def bottleneck_1d(filters, stage=0, block=0, kernel_size=3, numerical_name=False, stride=None, freeze_bn=False): 87 | """ 88 | A one-dimensional bottleneck block. 89 | 90 | :param filters: the output’s feature space 91 | 92 | :param stage: int representing the stage of this block (starting from 0) 93 | 94 | :param block: int representing this block (starting from 0) 95 | 96 | :param kernel_size: size of the kernel 97 | 98 | :param numerical_name: if true, uses numbers to represent blocks instead of chars (ResNet{101, 152, 200}) 99 | 100 | :param stride: int representing the stride used in the shortcut and the first conv layer, default derives stride from block id 101 | 102 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 103 | 104 | Usage: 105 | 106 | >>> import keras_resnet.blocks 107 | 108 | >>> keras_resnet.blocks.bottleneck_1d(64) 109 | """ 110 | if stride is None: 111 | stride = 1 if block != 0 or stage == 0 else 2 112 | 113 | if keras.backend.image_data_format() == "channels_last": 114 | axis = 3 115 | else: 116 | axis = 1 117 | 118 | if block > 0 and numerical_name: 119 | block_char = "b{}".format(block) 120 | else: 121 | block_char = chr(ord('a') + block) 122 | 123 | stage_char = str(stage + 2) 124 | 125 | def f(x): 126 | y = keras.layers.Conv1D(filters, (1, 1), strides=stride, use_bias=False, name="res{}{}_branch2a".format(stage_char, block_char), **parameters)(x) 127 | y = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2a".format(stage_char, block_char))(y) 128 | y = keras.layers.Activation("relu", name="res{}{}_branch2a_relu".format(stage_char, block_char))(y) 129 | 130 | y = keras.layers.ZeroPadding1D(padding=1, name="padding{}{}_branch2b".format(stage_char, block_char))(y) 131 | y = keras.layers.Conv1D(filters, kernel_size, use_bias=False, name="res{}{}_branch2b".format(stage_char, block_char), **parameters)(y) 132 | y = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2b".format(stage_char, block_char))(y) 133 | y = keras.layers.Activation("relu", name="res{}{}_branch2b_relu".format(stage_char, block_char))(y) 134 | 135 | y = keras.layers.Conv1D(filters * 4, (1, 1), use_bias=False, name="res{}{}_branch2c".format(stage_char, block_char), **parameters)(y) 136 | y = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2c".format(stage_char, block_char))(y) 137 | 138 | if block == 0: 139 | shortcut = keras.layers.Conv1D(filters * 4, (1, 1), strides=stride, use_bias=False, name="res{}{}_branch1".format(stage_char, block_char), **parameters)(x) 140 | shortcut = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch1".format(stage_char, block_char))(shortcut) 141 | else: 142 | shortcut = x 143 | 144 | y = keras.layers.Add(name="res{}{}".format(stage_char, block_char))([y, shortcut]) 145 | y = keras.layers.Activation("relu", name="res{}{}_relu".format(stage_char, block_char))(y) 146 | 147 | return y 148 | 149 | return f 150 | -------------------------------------------------------------------------------- /Detection/keras_resnet/blocks/_2d.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | keras_resnet.blocks._2d 5 | ~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module implements a number of popular two-dimensional residual blocks. 8 | """ 9 | 10 | import keras.layers 11 | import keras.regularizers 12 | from imageai.Detection import keras_resnet 13 | from imageai.Detection.keras_resnet import layers 14 | 15 | parameters = { 16 | "kernel_initializer": "he_normal" 17 | } 18 | 19 | 20 | def basic_2d(filters, stage=0, block=0, kernel_size=3, numerical_name=False, stride=None, freeze_bn=False): 21 | """ 22 | A two-dimensional basic block. 23 | 24 | :param filters: the output’s feature space 25 | 26 | :param stage: int representing the stage of this block (starting from 0) 27 | 28 | :param block: int representing this block (starting from 0) 29 | 30 | :param kernel_size: size of the kernel 31 | 32 | :param numerical_name: if true, uses numbers to represent blocks instead of chars (ResNet{101, 152, 200}) 33 | 34 | :param stride: int representing the stride used in the shortcut and the first conv layer, default derives stride from block id 35 | 36 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 37 | 38 | Usage: 39 | 40 | >>> import keras_resnet.blocks 41 | 42 | >>> keras_resnet.blocks.basic_2d(64) 43 | """ 44 | if stride is None: 45 | if block != 0 or stage == 0: 46 | stride = 1 47 | else: 48 | stride = 2 49 | 50 | if keras.backend.image_data_format() == "channels_last": 51 | axis = 3 52 | else: 53 | axis = 1 54 | 55 | if block > 0 and numerical_name: 56 | block_char = "b{}".format(block) 57 | else: 58 | block_char = chr(ord('a') + block) 59 | 60 | stage_char = str(stage + 2) 61 | 62 | def f(x): 63 | y = keras.layers.ZeroPadding2D(padding=1, name="padding{}{}_branch2a".format(stage_char, block_char))(x) 64 | y = keras.layers.Conv2D(filters, kernel_size, strides=stride, use_bias=False, name="res{}{}_branch2a".format(stage_char, block_char), **parameters)(y) 65 | y = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2a".format(stage_char, block_char))(y) 66 | y = keras.layers.Activation("relu", name="res{}{}_branch2a_relu".format(stage_char, block_char))(y) 67 | 68 | y = keras.layers.ZeroPadding2D(padding=1, name="padding{}{}_branch2b".format(stage_char, block_char))(y) 69 | y = keras.layers.Conv2D(filters, kernel_size, use_bias=False, name="res{}{}_branch2b".format(stage_char, block_char), **parameters)(y) 70 | y = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2b".format(stage_char, block_char))(y) 71 | 72 | if block == 0: 73 | shortcut = keras.layers.Conv2D(filters, (1, 1), strides=stride, use_bias=False, name="res{}{}_branch1".format(stage_char, block_char), **parameters)(x) 74 | shortcut = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch1".format(stage_char, block_char))(shortcut) 75 | else: 76 | shortcut = x 77 | 78 | y = keras.layers.Add(name="res{}{}".format(stage_char, block_char))([y, shortcut]) 79 | y = keras.layers.Activation("relu", name="res{}{}_relu".format(stage_char, block_char))(y) 80 | 81 | return y 82 | 83 | return f 84 | 85 | 86 | def bottleneck_2d(filters, stage=0, block=0, kernel_size=3, numerical_name=False, stride=None, freeze_bn=False): 87 | """ 88 | A two-dimensional bottleneck block. 89 | 90 | :param filters: the output’s feature space 91 | 92 | :param stage: int representing the stage of this block (starting from 0) 93 | 94 | :param block: int representing this block (starting from 0) 95 | 96 | :param kernel_size: size of the kernel 97 | 98 | :param numerical_name: if true, uses numbers to represent blocks instead of chars (ResNet{101, 152, 200}) 99 | 100 | :param stride: int representing the stride used in the shortcut and the first conv layer, default derives stride from block id 101 | 102 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 103 | 104 | Usage: 105 | 106 | >>> import keras_resnet.blocks 107 | 108 | >>> keras_resnet.blocks.bottleneck_2d(64) 109 | """ 110 | if stride is None: 111 | if block != 0 or stage == 0: 112 | stride = 1 113 | else: 114 | stride = 2 115 | 116 | if keras.backend.image_data_format() == "channels_last": 117 | axis = 3 118 | else: 119 | axis = 1 120 | 121 | if block > 0 and numerical_name: 122 | block_char = "b{}".format(block) 123 | else: 124 | block_char = chr(ord('a') + block) 125 | 126 | stage_char = str(stage + 2) 127 | 128 | def f(x): 129 | y = keras.layers.Conv2D(filters, (1, 1), strides=stride, use_bias=False, name="res{}{}_branch2a".format(stage_char, block_char), **parameters)(x) 130 | y = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2a".format(stage_char, block_char))(y) 131 | y = keras.layers.Activation("relu", name="res{}{}_branch2a_relu".format(stage_char, block_char))(y) 132 | 133 | y = keras.layers.ZeroPadding2D(padding=1, name="padding{}{}_branch2b".format(stage_char, block_char))(y) 134 | y = keras.layers.Conv2D(filters, kernel_size, use_bias=False, name="res{}{}_branch2b".format(stage_char, block_char), **parameters)(y) 135 | y = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2b".format(stage_char, block_char))(y) 136 | y = keras.layers.Activation("relu", name="res{}{}_branch2b_relu".format(stage_char, block_char))(y) 137 | 138 | y = keras.layers.Conv2D(filters * 4, (1, 1), use_bias=False, name="res{}{}_branch2c".format(stage_char, block_char), **parameters)(y) 139 | y = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2c".format(stage_char, block_char))(y) 140 | 141 | if block == 0: 142 | shortcut = keras.layers.Conv2D(filters * 4, (1, 1), strides=stride, use_bias=False, name="res{}{}_branch1".format(stage_char, block_char), **parameters)(x) 143 | shortcut = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch1".format(stage_char, block_char))(shortcut) 144 | else: 145 | shortcut = x 146 | 147 | y = keras.layers.Add(name="res{}{}".format(stage_char, block_char))([y, shortcut]) 148 | y = keras.layers.Activation("relu", name="res{}{}_relu".format(stage_char, block_char))(y) 149 | 150 | return y 151 | 152 | return f 153 | -------------------------------------------------------------------------------- /Detection/keras_resnet/blocks/_3d.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | keras_resnet.blocks._3d 5 | ~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module implements a number of popular three-dimensional residual blocks. 8 | """ 9 | 10 | import keras.layers 11 | import keras.regularizers 12 | from imageai.Detection import keras_resnet 13 | from imageai.Detection.keras_resnet import layers 14 | 15 | parameters = { 16 | "kernel_initializer": "he_normal" 17 | } 18 | 19 | 20 | def basic_3d(filters, stage=0, block=0, kernel_size=3, numerical_name=False, stride=None, freeze_bn=False): 21 | """ 22 | A three-dimensional basic block. 23 | 24 | :param filters: the output’s feature space 25 | 26 | :param stage: int representing the stage of this block (starting from 0) 27 | 28 | :param block: int representing this block (starting from 0) 29 | 30 | :param kernel_size: size of the kernel 31 | 32 | :param numerical_name: if true, uses numbers to represent blocks instead of chars (ResNet{101, 152, 200}) 33 | 34 | :param stride: int representing the stride used in the shortcut and the first conv layer, default derives stride from block id 35 | 36 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 37 | 38 | Usage: 39 | 40 | >>> import keras_resnet.blocks 41 | 42 | >>> keras_resnet.blocks.basic_3d(64) 43 | """ 44 | if stride is None: 45 | if block != 0 or stage == 0: 46 | stride = 1 47 | else: 48 | stride = 2 49 | 50 | if keras.backend.image_data_format() == "channels_last": 51 | axis = 3 52 | else: 53 | axis = 1 54 | 55 | if block > 0 and numerical_name: 56 | block_char = "b{}".format(block) 57 | else: 58 | block_char = chr(ord('a') + block) 59 | 60 | stage_char = str(stage + 2) 61 | 62 | def f(x): 63 | y = keras.layers.ZeroPadding3D(padding=1, name="padding{}{}_branch2a".format(stage_char, block_char))(x) 64 | y = keras.layers.Conv3D(filters, kernel_size, strides=stride, use_bias=False, name="res{}{}_branch2a".format(stage_char, block_char), **parameters)(y) 65 | y = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2a".format(stage_char, block_char))(y) 66 | y = keras.layers.Activation("relu", name="res{}{}_branch2a_relu".format(stage_char, block_char))(y) 67 | 68 | y = keras.layers.ZeroPadding3D(padding=1, name="padding{}{}_branch2b".format(stage_char, block_char))(y) 69 | y = keras.layers.Conv3D(filters, kernel_size, use_bias=False, name="res{}{}_branch2b".format(stage_char, block_char), **parameters)(y) 70 | y = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2b".format(stage_char, block_char))(y) 71 | 72 | if block == 0: 73 | shortcut = keras.layers.Conv3D(filters, (1, 1), strides=stride, use_bias=False, name="res{}{}_branch1".format(stage_char, block_char), **parameters)(x) 74 | shortcut = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch1".format(stage_char, block_char))(shortcut) 75 | else: 76 | shortcut = x 77 | 78 | y = keras.layers.Add(name="res{}{}".format(stage_char, block_char))([y, shortcut]) 79 | y = keras.layers.Activation("relu", name="res{}{}_relu".format(stage_char, block_char))(y) 80 | 81 | return y 82 | 83 | return f 84 | 85 | 86 | def bottleneck_3d(filters, stage=0, block=0, kernel_size=3, numerical_name=False, stride=None, freeze_bn=False): 87 | """ 88 | A three-dimensional bottleneck block. 89 | 90 | :param filters: the output’s feature space 91 | 92 | :param stage: int representing the stage of this block (starting from 0) 93 | 94 | :param block: int representing this block (starting from 0) 95 | 96 | :param kernel_size: size of the kernel 97 | 98 | :param numerical_name: if true, uses numbers to represent blocks instead of chars (ResNet{101, 152, 200}) 99 | 100 | :param stride: int representing the stride used in the shortcut and the first conv layer, default derives stride from block id 101 | 102 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 103 | 104 | Usage: 105 | 106 | >>> import keras_resnet.blocks 107 | 108 | >>> keras_resnet.blocks.bottleneck_3d(64) 109 | """ 110 | if stride is None: 111 | if block != 0 or stage == 0: 112 | stride = 1 113 | else: 114 | stride = 2 115 | 116 | if keras.backend.image_data_format() == "channels_last": 117 | axis = 3 118 | else: 119 | axis = 1 120 | 121 | if block > 0 and numerical_name: 122 | block_char = "b{}".format(block) 123 | else: 124 | block_char = chr(ord('a') + block) 125 | 126 | stage_char = str(stage + 2) 127 | 128 | def f(x): 129 | y = keras.layers.Conv3D(filters, (1, 1), strides=stride, use_bias=False, name="res{}{}_branch2a".format(stage_char, block_char), **parameters)(x) 130 | y = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2a".format(stage_char, block_char))(y) 131 | y = keras.layers.Activation("relu", name="res{}{}_branch2a_relu".format(stage_char, block_char))(y) 132 | 133 | y = keras.layers.ZeroPadding3D(padding=1, name="padding{}{}_branch2b".format(stage_char, block_char))(y) 134 | y = keras.layers.Conv3D(filters, kernel_size, use_bias=False, name="res{}{}_branch2b".format(stage_char, block_char), **parameters)(y) 135 | y = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2b".format(stage_char, block_char))(y) 136 | y = keras.layers.Activation("relu", name="res{}{}_branch2b_relu".format(stage_char, block_char))(y) 137 | 138 | y = keras.layers.Conv3D(filters * 4, (1, 1), use_bias=False, name="res{}{}_branch2c".format(stage_char, block_char), **parameters)(y) 139 | y = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch2c".format(stage_char, block_char))(y) 140 | 141 | if block == 0: 142 | shortcut = keras.layers.Conv3D(filters * 4, (1, 1), strides=stride, use_bias=False, name="res{}{}_branch1".format(stage_char, block_char), **parameters)(x) 143 | shortcut = keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn, name="bn{}{}_branch1".format(stage_char, block_char))(shortcut) 144 | else: 145 | shortcut = x 146 | 147 | y = keras.layers.Add(name="res{}{}".format(stage_char, block_char))([y, shortcut]) 148 | y = keras.layers.Activation("relu", name="res{}{}_relu".format(stage_char, block_char))(y) 149 | 150 | return y 151 | 152 | return f 153 | -------------------------------------------------------------------------------- /Detection/keras_resnet/blocks/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | keras_resnet.blocks 5 | ~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module implements a number of popular residual blocks. 8 | """ 9 | 10 | from ._1d import ( 11 | basic_1d, 12 | bottleneck_1d 13 | ) 14 | 15 | from ._2d import ( 16 | basic_2d, 17 | bottleneck_2d 18 | ) 19 | 20 | from ._3d import ( 21 | basic_3d, 22 | bottleneck_3d 23 | ) 24 | 25 | from ._time_distributed_2d import ( 26 | time_distributed_basic_2d, 27 | time_distributed_bottleneck_2d 28 | ) 29 | -------------------------------------------------------------------------------- /Detection/keras_resnet/blocks/__pycache__/_1d.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_resnet/blocks/__pycache__/_1d.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_resnet/blocks/__pycache__/_2d.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_resnet/blocks/__pycache__/_2d.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_resnet/blocks/__pycache__/_3d.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_resnet/blocks/__pycache__/_3d.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_resnet/blocks/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_resnet/blocks/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_resnet/blocks/__pycache__/_time_distributed_2d.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_resnet/blocks/__pycache__/_time_distributed_2d.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_resnet/blocks/_time_distributed_2d.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | keras_resnet.blocks._time_distributed_2d 5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module implements a number of popular time distributed two-dimensional residual blocks. 8 | """ 9 | 10 | import keras.layers 11 | import keras.regularizers 12 | from imageai.Detection import keras_resnet 13 | from imageai.Detection.keras_resnet import layers 14 | 15 | parameters = { 16 | "kernel_initializer": "he_normal" 17 | } 18 | 19 | 20 | def time_distributed_basic_2d(filters, stage=0, block=0, kernel_size=3, numerical_name=False, stride=None, freeze_bn=False): 21 | """ 22 | 23 | A time distributed two-dimensional basic block. 24 | 25 | :param filters: the output’s feature space 26 | 27 | :param stage: int representing the stage of this block (starting from 0) 28 | 29 | :param block: int representing this block (starting from 0) 30 | 31 | :param kernel_size: size of the kernel 32 | 33 | :param numerical_name: if true, uses numbers to represent blocks instead of chars (ResNet{101, 152, 200}) 34 | 35 | :param stride: int representing the stride used in the shortcut and the first conv layer, default derives stride from block id 36 | 37 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 38 | 39 | Usage: 40 | 41 | >>> import keras_resnet.blocks 42 | 43 | >>> keras_resnet.blocks.time_distributed_basic_2d(64) 44 | 45 | """ 46 | if stride is None: 47 | if block != 0 or stage == 0: 48 | stride = 1 49 | else: 50 | stride = 2 51 | 52 | if keras.backend.image_data_format() == "channels_last": 53 | axis = 3 54 | else: 55 | axis = 1 56 | 57 | if block > 0 and numerical_name: 58 | block_char = "b{}".format(block) 59 | else: 60 | block_char = chr(ord('a') + block) 61 | 62 | stage_char = str(stage + 2) 63 | 64 | def f(x): 65 | y = keras.layers.TimeDistributed(keras.layers.ZeroPadding2D(padding=1), name="padding{}{}_branch2a".format(stage_char, block_char))(x) 66 | y = keras.layers.TimeDistributed(keras.layers.Conv2D(filters, kernel_size, strides=stride, use_bias=False, **parameters), name="res{}{}_branch2a".format(stage_char, block_char))(y) 67 | y = keras.layers.TimeDistributed(keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn), name="bn{}{}_branch2a".format(stage_char, block_char))(y) 68 | y = keras.layers.TimeDistributed(keras.layers.Activation("relu"), name="res{}{}_branch2a_relu".format(stage_char, block_char))(y) 69 | 70 | y = keras.layers.TimeDistributed(keras.layers.ZeroPadding2D(padding=1), name="padding{}{}_branch2b".format(stage_char, block_char))(y) 71 | y = keras.layers.TimeDistributed(keras.layers.Conv2D(filters, kernel_size, use_bias=False, **parameters), name="res{}{}_branch2b".format(stage_char, block_char))(y) 72 | y = keras.layers.TimeDistributed(keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn), name="bn{}{}_branch2b".format(stage_char, block_char))(y) 73 | 74 | if block == 0: 75 | shortcut = keras.layers.TimeDistributed(keras.layers.Conv2D(filters, (1, 1), strides=stride, use_bias=False, **parameters), name="res{}{}_branch1".format(stage_char, block_char))(x) 76 | shortcut = keras.layers.TimeDistributed(keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn), name="bn{}{}_branch1".format(stage_char, block_char))(shortcut) 77 | else: 78 | shortcut = x 79 | 80 | y = keras.layers.Add(name="res{}{}".format(stage_char, block_char))([y, shortcut]) 81 | y = keras.layers.TimeDistributed(keras.layers.Activation("relu"), name="res{}{}_relu".format(stage_char, block_char))(y) 82 | 83 | return y 84 | 85 | return f 86 | 87 | 88 | def time_distributed_bottleneck_2d(filters, stage=0, block=0, kernel_size=3, numerical_name=False, stride=None, freeze_bn=False): 89 | """ 90 | 91 | A time distributed two-dimensional bottleneck block. 92 | 93 | :param filters: the output’s feature space 94 | 95 | :param stage: int representing the stage of this block (starting from 0) 96 | 97 | :param block: int representing this block (starting from 0) 98 | 99 | :param kernel_size: size of the kernel 100 | 101 | :param numerical_name: if true, uses numbers to represent blocks instead of chars (ResNet{101, 152, 200}) 102 | 103 | :param stride: int representing the stride used in the shortcut and the first conv layer, default derives stride from block id 104 | 105 | :param freeze_bn: if true, freezes BatchNormalization layers (ie. no updates are done in these layers) 106 | 107 | Usage: 108 | 109 | >>> import keras_resnet.blocks 110 | 111 | >>> keras_resnet.blocks.time_distributed_bottleneck_2d(64) 112 | 113 | """ 114 | if stride is None: 115 | if block != 0 or stage == 0: 116 | stride = 1 117 | else: 118 | stride = 2 119 | 120 | if keras.backend.image_data_format() == "channels_last": 121 | axis = 3 122 | else: 123 | axis = 1 124 | 125 | if block > 0 and numerical_name: 126 | block_char = "b{}".format(block) 127 | else: 128 | block_char = chr(ord('a') + block) 129 | 130 | stage_char = str(stage + 2) 131 | 132 | def f(x): 133 | y = keras.layers.TimeDistributed(keras.layers.Conv2D(filters, (1, 1), strides=stride, use_bias=False, **parameters), name="res{}{}_branch2a".format(stage_char, block_char))(x) 134 | y = keras.layers.TimeDistributed(keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn), name="bn{}{}_branch2a".format(stage_char, block_char))(y) 135 | y = keras.layers.TimeDistributed(keras.layers.Activation("relu"), name="res{}{}_branch2a_relu".format(stage_char, block_char))(y) 136 | 137 | y = keras.layers.TimeDistributed(keras.layers.ZeroPadding2D(padding=1), name="padding{}{}_branch2b".format(stage_char, block_char))(y) 138 | y = keras.layers.TimeDistributed(keras.layers.Conv2D(filters, kernel_size, use_bias=False, **parameters), name="res{}{}_branch2b".format(stage_char, block_char))(y) 139 | y = keras.layers.TimeDistributed(keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn), name="bn{}{}_branch2b".format(stage_char, block_char))(y) 140 | y = keras.layers.TimeDistributed(keras.layers.Activation("relu"), name="res{}{}_branch2b_relu".format(stage_char, block_char))(y) 141 | 142 | y = keras.layers.TimeDistributed(keras.layers.Conv2D(filters * 4, (1, 1), use_bias=False, **parameters), name="res{}{}_branch2c".format(stage_char, block_char))(y) 143 | y = keras.layers.TimeDistributed(keras_resnet.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn), name="bn{}{}_branch2c".format(stage_char, block_char))(y) 144 | 145 | if block == 0: 146 | shortcut = keras.layers.TimeDistributed(keras.layers.Conv2D(filters * 4, (1, 1), strides=stride, use_bias=False, **parameters), name="res{}{}_branch1".format(stage_char, block_char))(x) 147 | shortcut = keras.layers.TimeDistributed(keras.layers.BatchNormalization(axis=axis, epsilon=1e-5, freeze=freeze_bn), name="bn{}{}_branch1".format(stage_char, block_char))(shortcut) 148 | else: 149 | shortcut = x 150 | 151 | y = keras.layers.Add(name="res{}{}".format(stage_char, block_char))([y, shortcut]) 152 | y = keras.layers.TimeDistributed(keras.layers.Activation("relu"), name="res{}{}_relu".format(stage_char, block_char))(y) 153 | 154 | return y 155 | 156 | return f 157 | -------------------------------------------------------------------------------- /Detection/keras_resnet/classifiers/_2d.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | keras_resnet.classifiers 5 | ~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module implements popular residual two-dimensional classifiers. 8 | """ 9 | 10 | import keras.backend 11 | import keras.layers 12 | import keras.models 13 | import keras.regularizers 14 | from imageai.Detection import keras_resnet 15 | from imageai.Detection.keras_resnet import models 16 | 17 | 18 | class ResNet18(keras.models.Model): 19 | """ 20 | A :class:`ResNet18 ` object. 21 | 22 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 23 | 24 | Usage: 25 | 26 | >>> import keras_resnet.classifiers 27 | 28 | >>> shape, classes = (224, 224, 3), 1000 29 | 30 | >>> x = keras.layers.Input(shape) 31 | 32 | >>> model = keras_resnet.classifiers.ResNet18(x) 33 | 34 | >>> model.compile("adam", "categorical_crossentropy", ["accuracy"]) 35 | """ 36 | def __init__(self, inputs, classes): 37 | outputs = keras_resnet.models.ResNet18(inputs) 38 | 39 | outputs = keras.layers.Flatten()(outputs.output) 40 | 41 | outputs = keras.layers.Dense(classes, activation="softmax")(outputs) 42 | 43 | super(ResNet18, self).__init__(inputs, outputs) 44 | 45 | 46 | class ResNet34(keras.models.Model): 47 | """ 48 | A :class:`ResNet34 ` object. 49 | 50 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 51 | 52 | Usage: 53 | 54 | >>> import keras_resnet.classifiers 55 | 56 | >>> shape, classes = (224, 224, 3), 1000 57 | 58 | >>> x = keras.layers.Input(shape) 59 | 60 | >>> model = keras_resnet.classifiers.ResNet34(x) 61 | 62 | >>> model.compile("adam", "categorical_crossentropy", ["accuracy"]) 63 | """ 64 | def __init__(self, inputs, classes): 65 | outputs = keras_resnet.models.ResNet34(inputs) 66 | 67 | outputs = keras.layers.Flatten()(outputs.output) 68 | 69 | outputs = keras.layers.Dense(classes, activation="softmax")(outputs) 70 | 71 | super(ResNet34, self).__init__(inputs, outputs) 72 | 73 | 74 | class ResNet50(keras.models.Model): 75 | """ 76 | A :class:`ResNet50 ` object. 77 | 78 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 79 | 80 | Usage: 81 | 82 | >>> import keras_resnet.classifiers 83 | 84 | >>> shape, classes = (224, 224, 3), 1000 85 | 86 | >>> x = keras.layers.Input(shape) 87 | 88 | >>> model = keras_resnet.classifiers.ResNet50(x) 89 | 90 | >>> model.compile("adam", "categorical_crossentropy", ["accuracy"]) 91 | """ 92 | def __init__(self, inputs, classes): 93 | outputs = keras_resnet.models.ResNet50(inputs) 94 | 95 | outputs = keras.layers.Flatten()(outputs.output) 96 | 97 | outputs = keras.layers.Dense(classes, activation="softmax")(outputs) 98 | 99 | super(ResNet50, self).__init__(inputs, outputs) 100 | 101 | 102 | class ResNet101(keras.models.Model): 103 | """ 104 | A :class:`ResNet101 ` object. 105 | 106 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 107 | 108 | Usage: 109 | 110 | >>> import keras_resnet.classifiers 111 | 112 | >>> shape, classes = (224, 224, 3), 1000 113 | 114 | >>> x = keras.layers.Input(shape) 115 | 116 | >>> model = keras_resnet.classifiers.ResNet101(x) 117 | 118 | >>> model.compile("adam", "categorical_crossentropy", ["accuracy"]) 119 | """ 120 | def __init__(self, inputs, classes): 121 | outputs = keras_resnet.models.ResNet101(inputs) 122 | 123 | outputs = keras.layers.Flatten()(outputs.output) 124 | 125 | outputs = keras.layers.Dense(classes, activation="softmax")(outputs) 126 | 127 | super(ResNet101, self).__init__(inputs, outputs) 128 | 129 | 130 | class ResNet152(keras.models.Model): 131 | """ 132 | A :class:`ResNet152 ` object. 133 | 134 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 135 | 136 | Usage: 137 | 138 | >>> import keras_resnet.classifiers 139 | 140 | >>> shape, classes = (224, 224, 3), 1000 141 | 142 | >>> x = keras.layers.Input(shape) 143 | 144 | >>> model = keras_resnet.classifiers.ResNet152(x) 145 | 146 | >>> model.compile("adam", "categorical_crossentropy", ["accuracy"]) 147 | 148 | """ 149 | def __init__(self, inputs, classes): 150 | outputs = keras_resnet.models.ResNet152(inputs) 151 | 152 | outputs = keras.layers.Flatten()(outputs.output) 153 | 154 | outputs = keras.layers.Dense(classes, activation="softmax")(outputs) 155 | 156 | super(ResNet152, self).__init__(inputs, outputs) 157 | 158 | 159 | class ResNet200(keras.models.Model): 160 | """ 161 | A :class:`ResNet200 ` object. 162 | 163 | :param inputs: input tensor (e.g. an instance of `keras.layers.Input`) 164 | 165 | Usage: 166 | 167 | >>> import keras_resnet.classifiers 168 | 169 | >>> shape, classes = (224, 224, 3), 1000 170 | 171 | >>> x = keras.layers.Input(shape) 172 | 173 | >>> model = keras_resnet.classifiers.ResNet200(x) 174 | 175 | >>> model.compile("adam", "categorical_crossentropy", ["accuracy"]) 176 | """ 177 | def __init__(self, inputs, classes): 178 | outputs = keras_resnet.models.ResNet200(inputs) 179 | 180 | outputs = keras.layers.Flatten()(outputs.output) 181 | 182 | outputs = keras.layers.Dense(classes, activation="softmax")(outputs) 183 | 184 | super(ResNet200, self).__init__(inputs, outputs) 185 | -------------------------------------------------------------------------------- /Detection/keras_resnet/classifiers/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | keras_resnet.classifiers 5 | ~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module implements popular residual classifiers. 8 | """ 9 | 10 | from ._2d import ( 11 | ResNet18, 12 | ResNet34, 13 | ResNet50, 14 | ResNet101, 15 | ResNet152, 16 | ResNet200 17 | ) 18 | -------------------------------------------------------------------------------- /Detection/keras_resnet/layers/__init__.py: -------------------------------------------------------------------------------- 1 | from ._batch_normalization import BatchNormalization 2 | -------------------------------------------------------------------------------- /Detection/keras_resnet/layers/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_resnet/layers/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_resnet/layers/__pycache__/_batch_normalization.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_resnet/layers/__pycache__/_batch_normalization.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_resnet/layers/_batch_normalization.py: -------------------------------------------------------------------------------- 1 | import keras 2 | 3 | 4 | class BatchNormalization(keras.layers.BatchNormalization): 5 | """ 6 | Identical to keras.layers.BatchNormalization, but adds the option to freeze parameters. 7 | """ 8 | def __init__(self, freeze, *args, **kwargs): 9 | self.freeze = freeze 10 | super(BatchNormalization, self).__init__(*args, **kwargs) 11 | 12 | # set to non-trainable if freeze is true 13 | self.trainable = not self.freeze 14 | 15 | def call(self, *args, **kwargs): 16 | # return super.call, but set training 17 | return super(BatchNormalization, self).call(training=(not self.freeze), *args, **kwargs) 18 | 19 | def get_config(self): 20 | config = super(BatchNormalization, self).get_config() 21 | config.update({'freeze': self.freeze}) 22 | return config 23 | -------------------------------------------------------------------------------- /Detection/keras_resnet/models/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | keras_resnet.models 5 | ~~~~~~~~~~~~~~~~~~~ 6 | 7 | This module implements popular residual models. 8 | """ 9 | 10 | from ._2d import ( 11 | ResNet, 12 | ResNet18, 13 | ResNet34, 14 | ResNet50, 15 | ResNet101, 16 | ResNet152, 17 | ResNet200 18 | ) 19 | 20 | from ._time_distributed_2d import ( 21 | TimeDistributedResNet, 22 | TimeDistributedResNet18, 23 | TimeDistributedResNet34, 24 | TimeDistributedResNet50, 25 | TimeDistributedResNet101, 26 | TimeDistributedResNet152, 27 | TimeDistributedResNet200 28 | ) 29 | -------------------------------------------------------------------------------- /Detection/keras_resnet/models/__pycache__/_2d.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_resnet/models/__pycache__/_2d.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_resnet/models/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_resnet/models/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_resnet/models/__pycache__/_time_distributed_2d.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_resnet/models/__pycache__/_time_distributed_2d.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/__init__.py -------------------------------------------------------------------------------- /Detection/keras_retinanet/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/__pycache__/initializers.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/__pycache__/initializers.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/__pycache__/losses.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/__pycache__/losses.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/backend/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from .dynamic import * 4 | from .common import * 5 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/backend/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/backend/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/backend/__pycache__/common.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/backend/__pycache__/common.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/backend/__pycache__/dynamic.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/backend/__pycache__/dynamic.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/backend/__pycache__/tensorflow_backend.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/backend/__pycache__/tensorflow_backend.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/backend/common.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import keras.backend 18 | from .dynamic import meshgrid 19 | 20 | import numpy as np 21 | 22 | 23 | def bbox_transform_inv(boxes, deltas, mean=None, std=None): 24 | if mean is None: 25 | mean = [0, 0, 0, 0] 26 | if std is None: 27 | std = [0.1, 0.1, 0.2, 0.2] 28 | 29 | widths = boxes[:, :, 2] - boxes[:, :, 0] 30 | heights = boxes[:, :, 3] - boxes[:, :, 1] 31 | ctr_x = boxes[:, :, 0] + 0.5 * widths 32 | ctr_y = boxes[:, :, 1] + 0.5 * heights 33 | 34 | dx = deltas[:, :, 0] * std[0] + mean[0] 35 | dy = deltas[:, :, 1] * std[1] + mean[1] 36 | dw = deltas[:, :, 2] * std[2] + mean[2] 37 | dh = deltas[:, :, 3] * std[3] + mean[3] 38 | 39 | pred_ctr_x = ctr_x + dx * widths 40 | pred_ctr_y = ctr_y + dy * heights 41 | pred_w = keras.backend.exp(dw) * widths 42 | pred_h = keras.backend.exp(dh) * heights 43 | 44 | pred_boxes_x1 = pred_ctr_x - 0.5 * pred_w 45 | pred_boxes_y1 = pred_ctr_y - 0.5 * pred_h 46 | pred_boxes_x2 = pred_ctr_x + 0.5 * pred_w 47 | pred_boxes_y2 = pred_ctr_y + 0.5 * pred_h 48 | 49 | pred_boxes = keras.backend.stack([pred_boxes_x1, pred_boxes_y1, pred_boxes_x2, pred_boxes_y2], axis=2) 50 | 51 | return pred_boxes 52 | 53 | 54 | def shift(shape, stride, anchors): 55 | """ 56 | Produce shifted anchors based on shape of the map and stride size 57 | """ 58 | shift_x = (keras.backend.arange(0, shape[1], dtype=keras.backend.floatx()) + keras.backend.constant(0.5, dtype=keras.backend.floatx())) * stride 59 | shift_y = (keras.backend.arange(0, shape[0], dtype=keras.backend.floatx()) + keras.backend.constant(0.5, dtype=keras.backend.floatx())) * stride 60 | 61 | shift_x, shift_y = meshgrid(shift_x, shift_y) 62 | shift_x = keras.backend.reshape(shift_x, [-1]) 63 | shift_y = keras.backend.reshape(shift_y, [-1]) 64 | 65 | shifts = keras.backend.stack([ 66 | shift_x, 67 | shift_y, 68 | shift_x, 69 | shift_y 70 | ], axis=0) 71 | 72 | shifts = keras.backend.transpose(shifts) 73 | number_of_anchors = keras.backend.shape(anchors)[0] 74 | 75 | k = keras.backend.shape(shifts)[0] # number of base points = feat_h * feat_w 76 | 77 | shifted_anchors = keras.backend.reshape(anchors, [1, number_of_anchors, 4]) + keras.backend.cast(keras.backend.reshape(shifts, [k, 1, 4]), keras.backend.floatx()) 78 | shifted_anchors = keras.backend.reshape(shifted_anchors, [k * number_of_anchors, 4]) 79 | 80 | return shifted_anchors 81 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/backend/dynamic.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | _BACKEND = "tensorflow" 4 | 5 | if "KERAS_BACKEND" in os.environ: 6 | _backend = os.environ["KERAS_BACKEND"] 7 | 8 | backends = { 9 | "tensorflow" 10 | } 11 | 12 | assert _backend in backends 13 | 14 | _BACKEND = _backend 15 | 16 | if _BACKEND == "tensorflow": 17 | from .tensorflow_backend import * 18 | else: 19 | raise ValueError("Unknown backend: " + str(_BACKEND)) 20 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/backend/tensorflow_backend.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import tensorflow 18 | import keras 19 | 20 | 21 | def resize_images(*args, **kwargs): 22 | return tensorflow.image.resize_images(*args, **kwargs) 23 | 24 | 25 | def non_max_suppression(*args, **kwargs): 26 | return tensorflow.image.non_max_suppression(*args, **kwargs) 27 | 28 | 29 | def range(*args, **kwargs): 30 | return tensorflow.range(*args, **kwargs) 31 | 32 | 33 | def scatter_nd(*args, **kwargs): 34 | return tensorflow.scatter_nd(*args, **kwargs) 35 | 36 | 37 | def gather_nd(*args, **kwargs): 38 | return tensorflow.gather_nd(*args, **kwargs) 39 | 40 | 41 | def meshgrid(*args, **kwargs): 42 | return tensorflow.meshgrid(*args, **kwargs) 43 | 44 | 45 | def where(*args, **kwargs): 46 | return tensorflow.where(*args, **kwargs) 47 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/callbacks/__init__.py: -------------------------------------------------------------------------------- 1 | from .common import * 2 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/callbacks/coco.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import keras 18 | from ..utils.coco_eval import evaluate_coco 19 | 20 | 21 | class CocoEval(keras.callbacks.Callback): 22 | def __init__(self, generator, threshold=0.05): 23 | self.generator = generator 24 | self.threshold = threshold 25 | 26 | super(CocoEval, self).__init__() 27 | 28 | def on_epoch_end(self, epoch, logs={}): 29 | evaluate_coco(self.generator, self.model, self.threshold) 30 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/callbacks/common.py: -------------------------------------------------------------------------------- 1 | import keras.callbacks 2 | 3 | 4 | class RedirectModel(keras.callbacks.Callback): 5 | """Callback which wraps another callback, but executed on a different model. 6 | # Arguments 7 | callback: callback to wrap. 8 | model: model to use when executing callbacks. 9 | # Example 10 | ```python 11 | model = keras.models.load_model('model.h5') 12 | model_checkpoint = ModelCheckpoint(filepath='snapshot.h5') 13 | parallel_model = multi_gpu_model(model, gpus=2) 14 | parallel_model.fit(X_train, Y_train, callbacks=[RedirectModel(model_checkpoint, model)]) 15 | ``` 16 | """ 17 | 18 | def __init__(self, 19 | callback, 20 | model): 21 | super(RedirectModel, self).__init__() 22 | 23 | self.callback = callback 24 | self.redirect_model = model 25 | 26 | def on_epoch_begin(self, epoch, logs=None): 27 | self.callback.on_epoch_begin(epoch, logs=logs) 28 | 29 | def on_epoch_end(self, epoch, logs=None): 30 | self.callback.on_epoch_end(epoch, logs=logs) 31 | 32 | def on_batch_begin(self, batch, logs=None): 33 | self.callback.on_batch_begin(batch, logs=logs) 34 | 35 | def on_batch_end(self, batch, logs=None): 36 | self.callback.on_batch_end(batch, logs=logs) 37 | 38 | def on_train_begin(self, logs=None): 39 | # overwrite the model with our custom model 40 | self.callback.set_model(self.redirect_model) 41 | 42 | self.callback.on_train_begin(logs=logs) 43 | 44 | def on_train_end(self, logs=None): 45 | self.callback.on_train_end(logs=logs) 46 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/callbacks/eval.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import keras 18 | from ..utils.eval import evaluate 19 | 20 | 21 | class Evaluate(keras.callbacks.Callback): 22 | def __init__(self, generator, iou_threshold=0.5, score_threshold=0.05, max_detections=100, save_path=None, tensorboard=None, verbose=1): 23 | """ Evaluate a given dataset using a given model at the end of every epoch during training. 24 | 25 | # Arguments 26 | generator : The generator that represents the dataset to evaluate. 27 | iou_threshold : The threshold used to consider when a detection is positive or negative. 28 | score_threshold : The score confidence threshold to use for detections. 29 | max_detections : The maximum number of detections to use per image. 30 | save_path : The path to save images with visualized detections to. 31 | tensorboard : Instance of keras.callbacks.TensorBoard used to log the mAP value. 32 | verbose : Set the verbosity level, by default this is set to 1. 33 | """ 34 | self.generator = generator 35 | self.iou_threshold = iou_threshold 36 | self.score_threshold = score_threshold 37 | self.max_detections = max_detections 38 | self.save_path = save_path 39 | self.tensorboard = tensorboard 40 | self.verbose = verbose 41 | 42 | super(Evaluate, self).__init__() 43 | 44 | def on_epoch_end(self, epoch, logs={}): 45 | # run evaluation 46 | average_precisions = evaluate( 47 | self.generator, 48 | self.model, 49 | iou_threshold=self.iou_threshold, 50 | score_threshold=self.score_threshold, 51 | max_detections=self.max_detections, 52 | save_path=self.save_path 53 | ) 54 | 55 | self.mean_ap = sum(average_precisions.values()) / len(average_precisions) 56 | 57 | if self.tensorboard is not None and self.tensorboard.writer is not None: 58 | import tensorflow as tf 59 | summary = tf.Summary() 60 | summary_value = summary.value.add() 61 | summary_value.simple_value = self.mean_ap 62 | summary_value.tag = "mAP" 63 | self.tensorboard.writer.add_summary(summary, epoch) 64 | 65 | if self.verbose == 1: 66 | for label, average_precision in average_precisions.items(): 67 | print(self.generator.label_to_name(label), '{:.4f}'.format(average_precision)) 68 | print('mAP: {:.4f}'.format(self.mean_ap)) 69 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/initializers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import keras 18 | 19 | import numpy as np 20 | import math 21 | 22 | 23 | class PriorProbability(keras.initializers.Initializer): 24 | """ 25 | Initializer applies a prior probability. 26 | """ 27 | 28 | def __init__(self, probability=0.01): 29 | self.probability = probability 30 | 31 | def get_config(self): 32 | return { 33 | 'probability': self.probability 34 | } 35 | 36 | def __call__(self, shape, dtype=None): 37 | # set bias to -log((1 - p)/p) for foregound 38 | result = np.ones(shape, dtype=dtype) * -math.log((1 - self.probability) / self.probability) 39 | 40 | return result 41 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/layers/__init__.py: -------------------------------------------------------------------------------- 1 | from ._misc import NonMaximumSuppression, RegressBoxes, UpsampleLike, Anchors -------------------------------------------------------------------------------- /Detection/keras_retinanet/layers/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/layers/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/layers/__pycache__/_misc.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/layers/__pycache__/_misc.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/layers/_misc.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import keras 18 | from .. import backend 19 | from ..utils import anchors as utils_anchors 20 | 21 | import numpy as np 22 | 23 | 24 | class Anchors(keras.layers.Layer): 25 | def __init__(self, size, stride, ratios=None, scales=None, *args, **kwargs): 26 | self.size = size 27 | self.stride = stride 28 | self.ratios = ratios 29 | self.scales = scales 30 | 31 | if ratios is None: 32 | self.ratios = np.array([0.5, 1, 2], keras.backend.floatx()), 33 | elif isinstance(ratios, list): 34 | self.ratios = np.array(ratios) 35 | if scales is None: 36 | self.scales = np.array([2 ** 0, 2 ** (1.0 / 3.0), 2 ** (2.0 / 3.0)], keras.backend.floatx()), 37 | elif isinstance(scales, list): 38 | self.scales = np.array(scales) 39 | 40 | self.num_anchors = len(ratios) * len(scales) 41 | self.anchors = keras.backend.variable(utils_anchors.generate_anchors( 42 | base_size=size, 43 | ratios=ratios, 44 | scales=scales, 45 | )) 46 | 47 | super(Anchors, self).__init__(*args, **kwargs) 48 | 49 | def call(self, inputs, **kwargs): 50 | features = inputs 51 | features_shape = keras.backend.shape(features)[:3] 52 | 53 | # generate proposals from bbox deltas and shifted anchors 54 | anchors = backend.shift(features_shape[1:3], self.stride, self.anchors) 55 | anchors = keras.backend.tile(keras.backend.expand_dims(anchors, axis=0), (features_shape[0], 1, 1)) 56 | 57 | return anchors 58 | 59 | def compute_output_shape(self, input_shape): 60 | if None not in input_shape[1:]: 61 | total = np.prod(input_shape[1:3]) * self.num_anchors 62 | return (input_shape[0], total, 4) 63 | else: 64 | return (input_shape[0], None, 4) 65 | 66 | def get_config(self): 67 | config = super(Anchors, self).get_config() 68 | config.update({ 69 | 'size' : self.size, 70 | 'stride' : self.stride, 71 | 'ratios' : self.ratios.tolist(), 72 | 'scales' : self.scales.tolist(), 73 | }) 74 | 75 | return config 76 | 77 | 78 | class NonMaximumSuppression(keras.layers.Layer): 79 | def __init__(self, nms_threshold=0.5, score_threshold=0.05, max_boxes=300, *args, **kwargs): 80 | self.nms_threshold = nms_threshold 81 | self.score_threshold = score_threshold 82 | self.max_boxes = max_boxes 83 | super(NonMaximumSuppression, self).__init__(*args, **kwargs) 84 | 85 | def call(self, inputs, **kwargs): 86 | # TODO: support batch size > 1. 87 | boxes = inputs[0][0] 88 | classification = inputs[1][0] 89 | other = [i[0] for i in inputs[2:]] # can be any user-specified additional data 90 | indices = backend.range(keras.backend.shape(classification)[0]) 91 | selected_scores = [] 92 | 93 | # perform per class NMS 94 | for c in range(int(classification.shape[1])): 95 | scores = classification[:, c] 96 | 97 | # threshold based on score 98 | score_indices = backend.where(keras.backend.greater(scores, self.score_threshold)) 99 | score_indices = keras.backend.cast(score_indices, 'int32') 100 | boxes_ = backend.gather_nd(boxes, score_indices) 101 | scores = keras.backend.gather(scores, score_indices)[:, 0] 102 | 103 | # perform NMS 104 | nms_indices = backend.non_max_suppression(boxes_, scores, max_output_size=self.max_boxes, iou_threshold=self.nms_threshold) 105 | 106 | # filter set of original indices 107 | selected_indices = keras.backend.gather(score_indices, nms_indices) 108 | 109 | # mask original classification column, setting all suppressed values to 0 110 | scores = keras.backend.gather(scores, nms_indices) 111 | scores = backend.scatter_nd(selected_indices, scores, keras.backend.shape(classification[:, c])) 112 | scores = keras.backend.expand_dims(scores, axis=1) 113 | 114 | selected_scores.append(scores) 115 | 116 | # reconstruct the (suppressed) classification scores 117 | classification = keras.backend.concatenate(selected_scores, axis=1) 118 | 119 | # reconstruct into the expected output 120 | detections = keras.backend.concatenate([boxes, classification] + other, axis=1) 121 | 122 | return keras.backend.expand_dims(detections, axis=0) 123 | 124 | def compute_output_shape(self, input_shape): 125 | return (input_shape[0][0], input_shape[0][1], sum([i[2] for i in input_shape])) 126 | 127 | def get_config(self): 128 | config = super(NonMaximumSuppression, self).get_config() 129 | config.update({ 130 | 'nms_threshold' : self.nms_threshold, 131 | 'score_threshold' : self.score_threshold, 132 | 'max_boxes' : self.max_boxes, 133 | }) 134 | 135 | return config 136 | 137 | 138 | class UpsampleLike(keras.layers.Layer): 139 | def call(self, inputs, **kwargs): 140 | source, target = inputs 141 | target_shape = keras.backend.shape(target) 142 | return backend.resize_images(source, (target_shape[1], target_shape[2])) 143 | 144 | def compute_output_shape(self, input_shape): 145 | return (input_shape[0][0],) + input_shape[1][1:3] + (input_shape[0][-1],) 146 | 147 | 148 | class RegressBoxes(keras.layers.Layer): 149 | def __init__(self, mean=None, std=None, *args, **kwargs): 150 | if mean is None: 151 | mean = np.array([0, 0, 0, 0]) 152 | if std is None: 153 | std = np.array([0.1, 0.1, 0.2, 0.2]) 154 | 155 | if isinstance(mean, (list, tuple)): 156 | mean = np.array(mean) 157 | elif not isinstance(mean, np.ndarray): 158 | raise ValueError('Expected mean to be a np.ndarray, list or tuple. Received: {}'.format(type(mean))) 159 | 160 | if isinstance(std, (list, tuple)): 161 | std = np.array(std) 162 | elif not isinstance(std, np.ndarray): 163 | raise ValueError('Expected std to be a np.ndarray, list or tuple. Received: {}'.format(type(std))) 164 | 165 | self.mean = mean 166 | self.std = std 167 | super(RegressBoxes, self).__init__(*args, **kwargs) 168 | 169 | def call(self, inputs, **kwargs): 170 | anchors, regression = inputs 171 | return backend.bbox_transform_inv(anchors, regression, mean=self.mean, std=self.std) 172 | 173 | def compute_output_shape(self, input_shape): 174 | return input_shape[0] 175 | 176 | def get_config(self): 177 | config = super(RegressBoxes, self).get_config() 178 | config.update({ 179 | 'mean': self.mean.tolist(), 180 | 'std' : self.std.tolist(), 181 | }) 182 | 183 | return config 184 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/losses.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import keras 18 | from . import backend 19 | 20 | 21 | def focal(alpha=0.25, gamma=2.0): 22 | def _focal(y_true, y_pred): 23 | labels = y_true 24 | classification = y_pred 25 | 26 | # compute the divisor: for each image in the batch, we want the number of positive anchors 27 | 28 | # clip the labels to 0, 1 so that we ignore the "ignore" label (-1) in the divisor 29 | divisor = backend.where(keras.backend.less_equal(labels, 0), keras.backend.zeros_like(labels), labels) 30 | divisor = keras.backend.max(divisor, axis=2, keepdims=True) 31 | divisor = keras.backend.cast(divisor, keras.backend.floatx()) 32 | 33 | # compute the number of positive anchors 34 | divisor = keras.backend.sum(divisor, axis=1, keepdims=True) 35 | 36 | # ensure we do not divide by 0 37 | divisor = keras.backend.maximum(1.0, divisor) 38 | 39 | # compute the focal loss 40 | alpha_factor = keras.backend.ones_like(labels) * alpha 41 | alpha_factor = backend.where(keras.backend.equal(labels, 1), alpha_factor, 1 - alpha_factor) 42 | focal_weight = backend.where(keras.backend.equal(labels, 1), 1 - classification, classification) 43 | focal_weight = alpha_factor * focal_weight ** gamma 44 | 45 | cls_loss = focal_weight * keras.backend.binary_crossentropy(labels, classification) 46 | 47 | # normalise by the number of positive anchors for each entry in the minibatch 48 | cls_loss = cls_loss / divisor 49 | 50 | # filter out "ignore" anchors 51 | anchor_state = keras.backend.max(labels, axis=2) # -1 for ignore, 0 for background, 1 for object 52 | indices = backend.where(keras.backend.not_equal(anchor_state, -1)) 53 | 54 | cls_loss = backend.gather_nd(cls_loss, indices) 55 | 56 | # divide by the size of the minibatch 57 | return keras.backend.sum(cls_loss) / keras.backend.cast(keras.backend.shape(labels)[0], keras.backend.floatx()) 58 | 59 | return _focal 60 | 61 | 62 | def smooth_l1(sigma=3.0): 63 | sigma_squared = sigma ** 2 64 | 65 | def _smooth_l1(y_true, y_pred): 66 | # separate target and state 67 | regression = y_pred 68 | regression_target = y_true[:, :, :4] 69 | anchor_state = y_true[:, :, 4] 70 | 71 | # compute the divisor: for each image in the batch, we want the number of positive anchors 72 | divisor = backend.where(keras.backend.equal(anchor_state, 1), keras.backend.ones_like(anchor_state), keras.backend.zeros_like(anchor_state)) 73 | divisor = keras.backend.sum(divisor, axis=1, keepdims=True) 74 | divisor = keras.backend.maximum(1.0, divisor) 75 | 76 | # pad the tensor to have shape (batch_size, 1, 1) for future division 77 | divisor = keras.backend.expand_dims(divisor, axis=2) 78 | 79 | # compute smooth L1 loss 80 | # f(x) = 0.5 * (sigma * x)^2 if |x| < 1 / sigma / sigma 81 | # |x| - 0.5 / sigma / sigma otherwise 82 | regression_diff = regression - regression_target 83 | regression_diff = keras.backend.abs(regression_diff) 84 | regression_loss = backend.where( 85 | keras.backend.less(regression_diff, 1.0 / sigma_squared), 86 | 0.5 * sigma_squared * keras.backend.pow(regression_diff, 2), 87 | regression_diff - 0.5 / sigma_squared 88 | ) 89 | 90 | # normalise by the number of positive and negative anchors for each entry in the minibatch 91 | regression_loss = regression_loss / divisor 92 | 93 | # filter out "ignore" anchors 94 | indices = backend.where(keras.backend.equal(anchor_state, 1)) 95 | regression_loss = backend.gather_nd(regression_loss, indices) 96 | 97 | # divide by the size of the minibatch 98 | regression_loss = keras.backend.sum(regression_loss) / keras.backend.cast(keras.backend.shape(y_true)[0], keras.backend.floatx()) 99 | 100 | return regression_loss 101 | 102 | return _smooth_l1 103 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/models/__init__.py -------------------------------------------------------------------------------- /Detection/keras_retinanet/models/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/models/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/models/__pycache__/resnet.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/models/__pycache__/resnet.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/models/__pycache__/retinanet.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/models/__pycache__/retinanet.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/models/mobilenet.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 lvaleriu (https://github.com/lvaleriu/) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import keras 18 | from keras.applications.mobilenet import MobileNet, BASE_WEIGHT_PATH, get_file, relu6, DepthwiseConv2D 19 | 20 | from ..models import retinanet 21 | 22 | mobile_net_custom_objects = { 23 | 'relu6': relu6, 24 | 'DepthwiseConv2D': DepthwiseConv2D 25 | } 26 | 27 | custom_objects = retinanet.custom_objects.copy() 28 | custom_objects.update(mobile_net_custom_objects) 29 | 30 | allowed_backbones = ['mobilenet128', 'mobilenet160', 'mobilenet192', 'mobilenet224'] 31 | 32 | 33 | def download_imagenet(backbone): 34 | """ Download pre-trained weights for the specified backbone name. This name is in the format 35 | mobilenet{rows}_{alpha} where rows is the imagenet shape dimension and 'alpha' controls 36 | the width of the network. 37 | For more info check the explanation from the keras mobilenet script itself 38 | # Arguments 39 | backbone : Backbone name. 40 | """ 41 | 42 | alpha = float(backbone.split('_')[1]) 43 | rows = int(backbone.split('_')[0].replace('mobilenet', '')) 44 | 45 | # load weights 46 | if keras.backend.image_data_format() == 'channels_first': 47 | raise ValueError('Weights for "channels_last" format ' 48 | 'are not available.') 49 | if alpha == 1.0: 50 | alpha_text = '1_0' 51 | elif alpha == 0.75: 52 | alpha_text = '7_5' 53 | elif alpha == 0.50: 54 | alpha_text = '5_0' 55 | else: 56 | alpha_text = '2_5' 57 | 58 | model_name = 'mobilenet_{}_{}_tf_no_top.h5'.format(alpha_text, rows) 59 | weights_url = BASE_WEIGHT_PATH + model_name 60 | weights_path = get_file(model_name, weights_url, cache_subdir='models') 61 | 62 | return weights_path 63 | 64 | 65 | def validate_backbone(backbone): 66 | """ Validate the backbone choice. 67 | # Arguments 68 | backbone : Backbone name. 69 | """ 70 | 71 | backbone = backbone.split('_')[0] 72 | 73 | if backbone not in allowed_backbones: 74 | raise ValueError('Backbone (\'{}\') not in allowed backbones ({}).'.format(backbone, allowed_backbones)) 75 | 76 | 77 | def mobilenet_retinanet(num_classes, backbone='mobilenet224_1.0', inputs=None, modifier=None, **kwargs): 78 | alpha = float(backbone.split('_')[1]) 79 | 80 | # choose default input 81 | if inputs is None: 82 | inputs = keras.layers.Input((None, None, 3)) 83 | 84 | mobilenet = MobileNet(input_tensor=inputs, alpha=alpha, include_top=False, pooling=None, weights=None) 85 | 86 | # get last layer from each depthwise convolution blocks 3, 5, 11 and 13 87 | outputs = [mobilenet.get_layer(name='conv_pw_{}_relu'.format(i)).output for i in [3, 5, 11, 13]] 88 | 89 | # create the mobilenet backbone 90 | mobilenet = keras.models.Model(inputs=inputs, outputs=outputs, name=mobilenet.name) 91 | 92 | # invoke modifier if given 93 | if modifier: 94 | mobilenet = modifier(mobilenet) 95 | 96 | # create the full model 97 | model = retinanet.retinanet_bbox(inputs=inputs, num_classes=num_classes, backbone=mobilenet, **kwargs) 98 | 99 | return model 100 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/models/resnet.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import warnings 18 | 19 | import keras 20 | from imageai.Detection import keras_resnet 21 | from imageai.Detection.keras_resnet import models 22 | from ..models import retinanet 23 | 24 | resnet_filename = 'ResNet-{}-model.keras.h5' 25 | resnet_resource = 'https://github.com/fizyr/keras-models/releases/download/v0.0.1/{}'.format(resnet_filename) 26 | 27 | custom_objects = retinanet.custom_objects.copy() 28 | custom_objects.update(keras_resnet.custom_objects) 29 | 30 | allowed_backbones = ['resnet50', 'resnet101', 'resnet152'] 31 | 32 | 33 | def download_imagenet(backbone): 34 | validate_backbone(backbone) 35 | 36 | backbone = int(backbone.replace('resnet', '')) 37 | 38 | filename = resnet_filename.format(backbone) 39 | resource = resnet_resource.format(backbone) 40 | if backbone == 50: 41 | checksum = '3e9f4e4f77bbe2c9bec13b53ee1c2319' 42 | elif backbone == 101: 43 | checksum = '05dc86924389e5b401a9ea0348a3213c' 44 | elif backbone == 152: 45 | checksum = '6ee11ef2b135592f8031058820bb9e71' 46 | 47 | return keras.applications.imagenet_utils.get_file( 48 | filename, 49 | resource, 50 | cache_subdir='models', 51 | md5_hash=checksum 52 | ) 53 | 54 | 55 | def validate_backbone(backbone): 56 | if backbone not in allowed_backbones: 57 | raise ValueError('Backbone (\'{}\') not in allowed backbones ({}).'.format(backbone, allowed_backbones)) 58 | 59 | 60 | def resnet_retinanet(num_classes, backbone='resnet50', inputs=None, modifier=None, **kwargs): 61 | validate_backbone(backbone) 62 | 63 | # choose default input 64 | if inputs is None: 65 | inputs = keras.layers.Input(shape=(None, None, 3)) 66 | 67 | # create the resnet backbone 68 | if backbone == 'resnet50': 69 | resnet = keras_resnet.models.ResNet50(inputs, include_top=False, freeze_bn=True) 70 | elif backbone == 'resnet101': 71 | resnet = keras_resnet.models.ResNet101(inputs, include_top=False, freeze_bn=True) 72 | elif backbone == 'resnet152': 73 | resnet = keras_resnet.models.ResNet152(inputs, include_top=False, freeze_bn=True) 74 | 75 | # invoke modifier if given 76 | if modifier: 77 | resnet = modifier(resnet) 78 | 79 | # create the full model 80 | model = retinanet.retinanet_bbox(inputs=inputs, num_classes=num_classes, backbone=resnet, **kwargs) 81 | 82 | return model 83 | 84 | 85 | def resnet50_retinanet(num_classes, inputs=None, **kwargs): 86 | return resnet_retinanet(num_classes=num_classes, backbone='resnet50', inputs=inputs, **kwargs) 87 | 88 | 89 | def resnet101_retinanet(num_classes, inputs=None, **kwargs): 90 | return resnet_retinanet(num_classes=num_classes, backbone='resnet101', inputs=inputs, **kwargs) 91 | 92 | 93 | def resnet152_retinanet(num_classes, inputs=None, **kwargs): 94 | return resnet_retinanet(num_classes=num_classes, backbone='resnet152', inputs=inputs, **kwargs) 95 | 96 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/preprocessing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/preprocessing/__init__.py -------------------------------------------------------------------------------- /Detection/keras_retinanet/preprocessing/coco.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | from ..preprocessing.generator import Generator 18 | from ..utils.image import read_image_bgr 19 | 20 | import os 21 | import numpy as np 22 | 23 | from pycocotools.coco import COCO 24 | 25 | 26 | class CocoGenerator(Generator): 27 | def __init__(self, data_dir, set_name, **kwargs): 28 | self.data_dir = data_dir 29 | self.set_name = set_name 30 | self.coco = COCO(os.path.join(data_dir, 'annotations', 'instances_' + set_name + '.json')) 31 | self.image_ids = self.coco.getImgIds() 32 | 33 | self.load_classes() 34 | 35 | super(CocoGenerator, self).__init__(**kwargs) 36 | 37 | def load_classes(self): 38 | # load class names (name -> label) 39 | categories = self.coco.loadCats(self.coco.getCatIds()) 40 | categories.sort(key=lambda x: x['id']) 41 | 42 | self.classes = {} 43 | self.coco_labels = {} 44 | self.coco_labels_inverse = {} 45 | for c in categories: 46 | self.coco_labels[len(self.classes)] = c['id'] 47 | self.coco_labels_inverse[c['id']] = len(self.classes) 48 | self.classes[c['name']] = len(self.classes) 49 | 50 | # also load the reverse (label -> name) 51 | self.labels = {} 52 | for key, value in self.classes.items(): 53 | self.labels[value] = key 54 | 55 | def size(self): 56 | return len(self.image_ids) 57 | 58 | def num_classes(self): 59 | return len(self.classes) 60 | 61 | def name_to_label(self, name): 62 | return self.classes[name] 63 | 64 | def label_to_name(self, label): 65 | return self.labels[label] 66 | 67 | def coco_label_to_label(self, coco_label): 68 | return self.coco_labels_inverse[coco_label] 69 | 70 | def coco_label_to_name(self, coco_label): 71 | return self.label_to_name(self.coco_label_to_label(coco_label)) 72 | 73 | def label_to_coco_label(self, label): 74 | return self.coco_labels[label] 75 | 76 | def image_aspect_ratio(self, image_index): 77 | image = self.coco.loadImgs(self.image_ids[image_index])[0] 78 | return float(image['width']) / float(image['height']) 79 | 80 | def load_image(self, image_index): 81 | image_info = self.coco.loadImgs(self.image_ids[image_index])[0] 82 | path = os.path.join(self.data_dir, 'images', self.set_name, image_info['file_name']) 83 | return read_image_bgr(path) 84 | 85 | def load_annotations(self, image_index): 86 | # get ground truth annotations 87 | annotations_ids = self.coco.getAnnIds(imgIds=self.image_ids[image_index], iscrowd=False) 88 | annotations = np.zeros((0, 5)) 89 | 90 | # some images appear to miss annotations (like image with id 257034) 91 | if len(annotations_ids) == 0: 92 | return annotations 93 | 94 | # parse annotations 95 | coco_annotations = self.coco.loadAnns(annotations_ids) 96 | for idx, a in enumerate(coco_annotations): 97 | # some annotations have basically no width / height, skip them 98 | if a['bbox'][2] < 1 or a['bbox'][3] < 1: 99 | continue 100 | 101 | annotation = np.zeros((1, 5)) 102 | annotation[0, :4] = a['bbox'] 103 | annotation[0, 4] = self.coco_label_to_label(a['category_id']) 104 | annotations = np.append(annotations, annotation, axis=0) 105 | 106 | # transform from [x, y, w, h] to [x1, y1, x2, y2] 107 | annotations[:, 2] = annotations[:, 0] + annotations[:, 2] 108 | annotations[:, 3] = annotations[:, 1] + annotations[:, 3] 109 | 110 | return annotations 111 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/preprocessing/csv_generator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 yhenon (https://github.com/yhenon/) 3 | Copyright 2017-2018 Fizyr (https://fizyr.com) 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | """ 17 | 18 | from .generator import Generator 19 | from ..utils.image import read_image_bgr 20 | 21 | import numpy as np 22 | from PIL import Image 23 | from six import raise_from 24 | 25 | import csv 26 | import sys 27 | import os.path 28 | 29 | 30 | def _parse(value, function, fmt): 31 | """ 32 | Parse a string into a value, and format a nice ValueError if it fails. 33 | 34 | Returns `function(value)`. 35 | Any `ValueError` raised is catched and a new `ValueError` is raised 36 | with message `fmt.format(e)`, where `e` is the caught `ValueError`. 37 | """ 38 | try: 39 | return function(value) 40 | except ValueError as e: 41 | raise_from(ValueError(fmt.format(e)), None) 42 | 43 | 44 | def _read_classes(csv_reader): 45 | result = {} 46 | for line, row in enumerate(csv_reader): 47 | try: 48 | class_name, class_id = row 49 | except ValueError: 50 | raise_from(ValueError('line {}: format should be \'class_name,class_id\''.format(line)), None) 51 | class_id = _parse(class_id, int, 'line {}: malformed class ID: {{}}'.format(line)) 52 | 53 | if class_name in result: 54 | raise ValueError('line {}: duplicate class name: \'{}\''.format(line, class_name)) 55 | result[class_name] = class_id 56 | return result 57 | 58 | 59 | def _read_annotations(csv_reader, classes): 60 | result = {} 61 | for line, row in enumerate(csv_reader): 62 | try: 63 | img_file, x1, y1, x2, y2, class_name = row 64 | except ValueError: 65 | raise_from(ValueError('line {}: format should be \'img_file,x1,y1,x2,y2,class_name\' or \'img_file,,,,,\''.format(line)), None) 66 | 67 | if img_file not in result: 68 | result[img_file] = [] 69 | 70 | # If a row contains only an image path, it's an image without annotations. 71 | if (x1, y1, x2, y2, class_name) == ('', '', '', '', ''): 72 | continue 73 | 74 | x1 = _parse(x1, int, 'line {}: malformed x1: {{}}'.format(line)) 75 | y1 = _parse(y1, int, 'line {}: malformed y1: {{}}'.format(line)) 76 | x2 = _parse(x2, int, 'line {}: malformed x2: {{}}'.format(line)) 77 | y2 = _parse(y2, int, 'line {}: malformed y2: {{}}'.format(line)) 78 | 79 | # Check that the bounding box is valid. 80 | if x2 <= x1: 81 | raise ValueError('line {}: x2 ({}) must be higher than x1 ({})'.format(line, x2, x1)) 82 | if y2 <= y1: 83 | raise ValueError('line {}: y2 ({}) must be higher than y1 ({})'.format(line, y2, y1)) 84 | 85 | # check if the current class name is correctly present 86 | if class_name not in classes: 87 | raise ValueError('line {}: unknown class name: \'{}\' (classes: {})'.format(line, class_name, classes)) 88 | 89 | result[img_file].append({'x1': x1, 'x2': x2, 'y1': y1, 'y2': y2, 'class': class_name}) 90 | return result 91 | 92 | 93 | def _open_for_csv(path): 94 | """ 95 | Open a file with flags suitable for csv.reader. 96 | 97 | This is different for python2 it means with mode 'rb', 98 | for python3 this means 'r' with "universal newlines". 99 | """ 100 | if sys.version_info[0] < 3: 101 | return open(path, 'rb') 102 | else: 103 | return open(path, 'r', newline='') 104 | 105 | 106 | class CSVGenerator(Generator): 107 | def __init__( 108 | self, 109 | csv_data_file, 110 | csv_class_file, 111 | base_dir=None, 112 | **kwargs 113 | ): 114 | self.image_names = [] 115 | self.image_data = {} 116 | self.base_dir = base_dir 117 | 118 | # Take base_dir from annotations file if not explicitly specified. 119 | if self.base_dir is None: 120 | self.base_dir = os.path.dirname(csv_data_file) 121 | 122 | # parse the provided class file 123 | try: 124 | with _open_for_csv(csv_class_file) as file: 125 | self.classes = _read_classes(csv.reader(file, delimiter=',')) 126 | except ValueError as e: 127 | raise_from(ValueError('invalid CSV class file: {}: {}'.format(csv_class_file, e)), None) 128 | 129 | self.labels = {} 130 | for key, value in self.classes.items(): 131 | self.labels[value] = key 132 | 133 | # csv with img_path, x1, y1, x2, y2, class_name 134 | try: 135 | with _open_for_csv(csv_data_file) as file: 136 | self.image_data = _read_annotations(csv.reader(file, delimiter=','), self.classes) 137 | except ValueError as e: 138 | raise_from(ValueError('invalid CSV annotations file: {}: {}'.format(csv_data_file, e)), None) 139 | self.image_names = list(self.image_data.keys()) 140 | 141 | super(CSVGenerator, self).__init__(**kwargs) 142 | 143 | def size(self): 144 | return len(self.image_names) 145 | 146 | def num_classes(self): 147 | return max(self.classes.values()) + 1 148 | 149 | def name_to_label(self, name): 150 | return self.classes[name] 151 | 152 | def label_to_name(self, label): 153 | return self.labels[label] 154 | 155 | def image_path(self, image_index): 156 | return os.path.join(self.base_dir, self.image_names[image_index]) 157 | 158 | def image_aspect_ratio(self, image_index): 159 | # PIL is fast for metadata 160 | image = Image.open(self.image_path(image_index)) 161 | return float(image.width) / float(image.height) 162 | 163 | def load_image(self, image_index): 164 | return read_image_bgr(self.image_path(image_index)) 165 | 166 | def load_annotations(self, image_index): 167 | path = self.image_names[image_index] 168 | annots = self.image_data[path] 169 | boxes = np.zeros((len(annots), 5)) 170 | 171 | for idx, annot in enumerate(annots): 172 | class_name = annot['class'] 173 | boxes[idx, 0] = float(annot['x1']) 174 | boxes[idx, 1] = float(annot['y1']) 175 | boxes[idx, 2] = float(annot['x2']) 176 | boxes[idx, 3] = float(annot['y2']) 177 | boxes[idx, 4] = self.name_to_label(class_name) 178 | 179 | return boxes 180 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/preprocessing/generator.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import numpy as np 18 | import random 19 | import threading 20 | import time 21 | import warnings 22 | 23 | import keras 24 | 25 | from ..utils.anchors import anchor_targets_bbox, bbox_transform 26 | from ..utils.image import ( 27 | TransformParameters, 28 | adjust_transform_for_image, 29 | apply_transform, 30 | preprocess_image, 31 | resize_image, 32 | ) 33 | from ..utils.transform import transform_aabb 34 | 35 | 36 | class Generator(object): 37 | def __init__( 38 | self, 39 | transform_generator = None, 40 | batch_size=1, 41 | group_method='ratio', # one of 'none', 'random', 'ratio' 42 | shuffle_groups=True, 43 | image_min_side=800, 44 | image_max_side=1333, 45 | transform_parameters=None, 46 | ): 47 | self.transform_generator = transform_generator 48 | self.batch_size = int(batch_size) 49 | self.group_method = group_method 50 | self.shuffle_groups = shuffle_groups 51 | self.image_min_side = image_min_side 52 | self.image_max_side = image_max_side 53 | self.transform_parameters = transform_parameters or TransformParameters() 54 | 55 | self.group_index = 0 56 | self.lock = threading.Lock() 57 | 58 | self.group_images() 59 | 60 | def size(self): 61 | raise NotImplementedError('size method not implemented') 62 | 63 | def num_classes(self): 64 | raise NotImplementedError('num_classes method not implemented') 65 | 66 | def name_to_label(self, name): 67 | raise NotImplementedError('name_to_label method not implemented') 68 | 69 | def label_to_name(self, label): 70 | raise NotImplementedError('label_to_name method not implemented') 71 | 72 | def image_aspect_ratio(self, image_index): 73 | raise NotImplementedError('image_aspect_ratio method not implemented') 74 | 75 | def load_image(self, image_index): 76 | raise NotImplementedError('load_image method not implemented') 77 | 78 | def load_annotations(self, image_index): 79 | raise NotImplementedError('load_annotations method not implemented') 80 | 81 | def load_annotations_group(self, group): 82 | return [self.load_annotations(image_index) for image_index in group] 83 | 84 | def filter_annotations(self, image_group, annotations_group, group): 85 | # test all annotations 86 | for index, (image, annotations) in enumerate(zip(image_group, annotations_group)): 87 | assert(isinstance(annotations, np.ndarray)), '\'load_annotations\' should return a list of numpy arrays, received: {}'.format(type(annotations)) 88 | 89 | # test x2 < x1 | y2 < y1 | x1 < 0 | y1 < 0 | x2 <= 0 | y2 <= 0 | x2 >= image.shape[1] | y2 >= image.shape[0] 90 | invalid_indices = np.where( 91 | (annotations[:, 2] <= annotations[:, 0]) | 92 | (annotations[:, 3] <= annotations[:, 1]) | 93 | (annotations[:, 0] < 0) | 94 | (annotations[:, 1] < 0) | 95 | (annotations[:, 2] > image.shape[1]) | 96 | (annotations[:, 3] > image.shape[0]) 97 | )[0] 98 | 99 | # delete invalid indices 100 | if len(invalid_indices): 101 | warnings.warn('Image with id {} (shape {}) contains the following invalid boxes: {}.'.format( 102 | group[index], 103 | image.shape, 104 | [annotations[invalid_index, :] for invalid_index in invalid_indices] 105 | )) 106 | annotations_group[index] = np.delete(annotations, invalid_indices, axis=0) 107 | 108 | return image_group, annotations_group 109 | 110 | def load_image_group(self, group): 111 | return [self.load_image(image_index) for image_index in group] 112 | 113 | def random_transform_group_entry(self, image, annotations): 114 | # randomly transform both image and annotations 115 | if self.transform_generator: 116 | transform = adjust_transform_for_image(next(self.transform_generator), image, self.transform_parameters.relative_translation) 117 | image = apply_transform(transform, image, self.transform_parameters) 118 | 119 | # Transform the bounding boxes in the annotations. 120 | annotations = annotations.copy() 121 | for index in range(annotations.shape[0]): 122 | annotations[index, :4] = transform_aabb(transform, annotations[index, :4]) 123 | 124 | return image, annotations 125 | 126 | def resize_image(self, image): 127 | return resize_image(image, min_side=self.image_min_side, max_side=self.image_max_side) 128 | 129 | def preprocess_image(self, image): 130 | return preprocess_image(image) 131 | 132 | def preprocess_group_entry(self, image, annotations): 133 | # preprocess the image 134 | image = self.preprocess_image(image) 135 | 136 | # randomly transform image and annotations 137 | image, annotations = self.random_transform_group_entry(image, annotations) 138 | 139 | # resize image 140 | image, image_scale = self.resize_image(image) 141 | 142 | # apply resizing to annotations too 143 | annotations[:, :4] *= image_scale 144 | 145 | return image, annotations 146 | 147 | def preprocess_group(self, image_group, annotations_group): 148 | for index, (image, annotations) in enumerate(zip(image_group, annotations_group)): 149 | # preprocess a single group entry 150 | image, annotations = self.preprocess_group_entry(image, annotations) 151 | 152 | # copy processed data back to group 153 | image_group[index] = image 154 | annotations_group[index] = annotations 155 | 156 | return image_group, annotations_group 157 | 158 | def group_images(self): 159 | # determine the order of the images 160 | order = list(range(self.size())) 161 | if self.group_method == 'random': 162 | random.shuffle(order) 163 | elif self.group_method == 'ratio': 164 | order.sort(key=lambda x: self.image_aspect_ratio(x)) 165 | 166 | # divide into groups, one group = one batch 167 | self.groups = [[order[x % len(order)] for x in range(i, i + self.batch_size)] for i in range(0, len(order), self.batch_size)] 168 | 169 | def compute_inputs(self, image_group): 170 | # get the max image shape 171 | max_shape = tuple(max(image.shape[x] for image in image_group) for x in range(3)) 172 | 173 | # construct an image batch object 174 | image_batch = np.zeros((self.batch_size,) + max_shape, dtype=keras.backend.floatx()) 175 | 176 | # copy all images to the upper left part of the image batch object 177 | for image_index, image in enumerate(image_group): 178 | image_batch[image_index, :image.shape[0], :image.shape[1], :image.shape[2]] = image 179 | 180 | return image_batch 181 | 182 | def anchor_targets( 183 | self, 184 | image_shape, 185 | annotations, 186 | num_classes, 187 | mask_shape=None, 188 | negative_overlap=0.4, 189 | positive_overlap=0.5, 190 | **kwargs 191 | ): 192 | return anchor_targets_bbox(image_shape, annotations, num_classes, mask_shape, negative_overlap, positive_overlap, **kwargs) 193 | 194 | def compute_targets(self, image_group, annotations_group): 195 | # get the max image shape 196 | max_shape = tuple(max(image.shape[x] for image in image_group) for x in range(3)) 197 | 198 | # compute labels and regression targets 199 | labels_group = [None] * self.batch_size 200 | regression_group = [None] * self.batch_size 201 | for index, (image, annotations) in enumerate(zip(image_group, annotations_group)): 202 | # compute regression targets 203 | labels_group[index], annotations, anchors = self.anchor_targets(max_shape, annotations, self.num_classes(), mask_shape=image.shape) 204 | regression_group[index] = bbox_transform(anchors, annotations) 205 | 206 | # append anchor states to regression targets (necessary for filtering 'ignore', 'positive' and 'negative' anchors) 207 | anchor_states = np.max(labels_group[index], axis=1, keepdims=True) 208 | regression_group[index] = np.append(regression_group[index], anchor_states, axis=1) 209 | 210 | labels_batch = np.zeros((self.batch_size,) + labels_group[0].shape, dtype=keras.backend.floatx()) 211 | regression_batch = np.zeros((self.batch_size,) + regression_group[0].shape, dtype=keras.backend.floatx()) 212 | 213 | # copy all labels and regression values to the batch blob 214 | for index, (labels, regression) in enumerate(zip(labels_group, regression_group)): 215 | labels_batch[index, ...] = labels 216 | regression_batch[index, ...] = regression 217 | 218 | return [regression_batch, labels_batch] 219 | 220 | def compute_input_output(self, group): 221 | # load images and annotations 222 | image_group = self.load_image_group(group) 223 | annotations_group = self.load_annotations_group(group) 224 | 225 | # check validity of annotations 226 | image_group, annotations_group = self.filter_annotations(image_group, annotations_group, group) 227 | 228 | # perform preprocessing steps 229 | image_group, annotations_group = self.preprocess_group(image_group, annotations_group) 230 | 231 | # compute network inputs 232 | inputs = self.compute_inputs(image_group) 233 | 234 | # compute network targets 235 | targets = self.compute_targets(image_group, annotations_group) 236 | 237 | return inputs, targets 238 | 239 | def __next__(self): 240 | return self.next() 241 | 242 | def next(self): 243 | # advance the group index 244 | with self.lock: 245 | if self.group_index == 0 and self.shuffle_groups: 246 | # shuffle groups at start of epoch 247 | random.shuffle(self.groups) 248 | group = self.groups[self.group_index] 249 | self.group_index = (self.group_index + 1) % len(self.groups) 250 | 251 | return self.compute_input_output(group) 252 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/preprocessing/kitti.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 lvaleriu (https://github.com/lvaleriu/) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import csv 18 | import os.path 19 | 20 | import numpy as np 21 | from PIL import Image 22 | 23 | from .generator import Generator 24 | from ..utils.image import read_image_bgr 25 | 26 | kitti_classes = { 27 | 'Car': 0, 28 | 'Van': 1, 29 | 'Truck': 2, 30 | 'Pedestrian': 3, 31 | 'Person_sitting': 4, 32 | 'Cyclist': 5, 33 | 'Tram': 6, 34 | 'Misc': 7, 35 | 'DontCare': 7 36 | } 37 | 38 | 39 | class KittiGenerator(Generator): 40 | def __init__( 41 | self, 42 | base_dir, 43 | subset='train', 44 | **kwargs 45 | ): 46 | self.base_dir = base_dir 47 | 48 | label_dir = os.path.join(self.base_dir, subset, 'labels') 49 | image_dir = os.path.join(self.base_dir, subset, 'images') 50 | 51 | """ 52 | 1 type Describes the type of object: 'Car', 'Van', 'Truck', 53 | 'Pedestrian', 'Person_sitting', 'Cyclist', 'Tram', 54 | 'Misc' or 'DontCare' 55 | 1 truncated Float from 0 (non-truncated) to 1 (truncated), where 56 | truncated refers to the object leaving image boundaries 57 | 1 occluded Integer (0,1,2,3) indicating occlusion state: 58 | 0 = fully visible, 1 = partly occluded 59 | 2 = largely occluded, 3 = unknown 60 | 1 alpha Observation angle of object, ranging [-pi..pi] 61 | 4 bbox 2D bounding box of object in the image (0-based index): 62 | contains left, top, right, bottom pixel coordinates 63 | 3 dimensions 3D object dimensions: height, width, length (in meters) 64 | 3 location 3D object location x,y,z in camera coordinates (in meters) 65 | 1 rotation_y Rotation ry around Y-axis in camera coordinates [-pi..pi] 66 | """ 67 | 68 | self.id_to_labels = {} 69 | for label, id in kitti_classes.items(): 70 | self.id_to_labels[id] = label 71 | 72 | self.image_data = dict() 73 | self.images = [] 74 | for i, fn in enumerate(os.listdir(label_dir)): 75 | label_fp = os.path.join(label_dir, fn) 76 | image_fp = os.path.join(image_dir, fn.replace('.txt', '.png')) 77 | 78 | self.images.append(image_fp) 79 | 80 | fieldnames = ['type', 'truncated', 'occluded', 'alpha', 'left', 'top', 'right', 'bottom', 'dh', 'dw', 'dl', 81 | 'lx', 'ly', 'lz', 'ry'] 82 | with open(label_fp, 'r') as csv_file: 83 | reader = csv.DictReader(csv_file, delimiter=' ', fieldnames=fieldnames) 84 | boxes = [] 85 | for line, row in enumerate(reader): 86 | label = row['type'] 87 | cls_id = kitti_classes[label] 88 | 89 | annotation = {'cls_id': cls_id, 'x1': row['left'], 'x2': row['right'], 'y2': row['bottom'], 'y1': row['top']} 90 | boxes.append(annotation) 91 | 92 | self.image_data[i] = boxes 93 | 94 | super(KittiGenerator, self).__init__(**kwargs) 95 | 96 | def size(self): 97 | return len(self.images) 98 | 99 | def num_classes(self): 100 | return max(kitti_classes.values()) + 1 101 | 102 | def name_to_label(self, name): 103 | raise NotImplementedError() 104 | 105 | def label_to_name(self, label): 106 | return self.id_to_labels[label] 107 | 108 | def image_aspect_ratio(self, image_index): 109 | # PIL is fast for metadata 110 | image = Image.open(self.images[image_index]) 111 | return float(image.width) / float(image.height) 112 | 113 | def load_image(self, image_index): 114 | return read_image_bgr(self.images[image_index]) 115 | 116 | def load_annotations(self, image_index): 117 | annotations = self.image_data[image_index] 118 | 119 | boxes = np.zeros((len(annotations), 5)) 120 | for idx, ann in enumerate(annotations): 121 | boxes[idx, 0] = float(ann['x1']) 122 | boxes[idx, 1] = float(ann['y1']) 123 | boxes[idx, 2] = float(ann['x2']) 124 | boxes[idx, 3] = float(ann['y2']) 125 | boxes[idx, 4] = int(ann['cls_id']) 126 | return boxes 127 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/preprocessing/open_images.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 lvaleriu (https://github.com/lvaleriu/) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import csv 18 | import json 19 | import os 20 | import warnings 21 | 22 | import numpy as np 23 | from PIL import Image 24 | 25 | from .generator import Generator 26 | from ..utils.image import read_image_bgr 27 | 28 | 29 | def get_labels(metadata_dir): 30 | trainable_classes_path = os.path.join(metadata_dir, 'classes-bbox-trainable.txt') 31 | description_path = os.path.join(metadata_dir, 'class-descriptions.csv') 32 | 33 | description_table = {} 34 | with open(description_path) as f: 35 | for row in csv.reader(f): 36 | # make sure the csv row is not empty (usually the last one) 37 | if len(row): 38 | description_table[row[0]] = row[1].replace("\"", "").replace("'", "").replace('`', '') 39 | 40 | with open(trainable_classes_path, 'rb') as f: 41 | trainable_classes = f.read().split('\n') 42 | 43 | id_to_labels = dict([(i, description_table[c]) for i, c in enumerate(trainable_classes)]) 44 | cls_index = dict([(c, i) for i, c in enumerate(trainable_classes)]) 45 | 46 | return id_to_labels, cls_index 47 | 48 | 49 | def generate_images_annotations_json(main_dir, metadata_dir, subset, cls_index): 50 | annotations_path = os.path.join(metadata_dir, subset, 'annotations-human-bbox.csv') 51 | 52 | cnt = 0 53 | with open(annotations_path, 'r') as csv_file: 54 | reader = csv.DictReader(csv_file, 55 | fieldnames=['ImageID', 'Source', 'LabelName', 56 | 'Confidence', 'XMin', 'XMax', 'YMin', 57 | 'YMax']) 58 | reader.next() 59 | for _ in reader: 60 | cnt += 1 61 | 62 | id_annotations = dict() 63 | with open(annotations_path, 'r') as csv_file: 64 | reader = csv.DictReader(csv_file, 65 | fieldnames=['ImageID', 'Source', 'LabelName', 66 | 'Confidence', 'XMin', 'XMax', 'YMin', 67 | 'YMax']) 68 | reader.next() 69 | 70 | images_sizes = {} 71 | for line, row in enumerate(reader): 72 | frame = row['ImageID'] 73 | class_name = row['LabelName'] 74 | 75 | if class_name not in cls_index: 76 | continue 77 | 78 | cls_id = cls_index[class_name] 79 | 80 | img_path = os.path.join(main_dir, 'images', subset, frame + '.jpg') 81 | if frame in images_sizes: 82 | width, height = images_sizes[frame] 83 | else: 84 | try: 85 | with Image.open(img_path) as img: 86 | width, height = img.width, img.height 87 | images_sizes[frame] = (width, height) 88 | except Exception: 89 | continue 90 | 91 | x1 = float(row['XMin']) 92 | x2 = float(row['XMax']) 93 | y1 = float(row['YMin']) 94 | y2 = float(row['YMax']) 95 | 96 | x1_int = int(round(x1 * width)) 97 | x2_int = int(round(x2 * width)) 98 | y1_int = int(round(y1 * height)) 99 | y2_int = int(round(y2 * height)) 100 | 101 | # Check that the bounding box is valid. 102 | if x2 <= x1: 103 | raise ValueError('line {}: x2 ({}) must be higher than x1 ({})'.format(line, x2, x1)) 104 | if y2 <= y1: 105 | raise ValueError('line {}: y2 ({}) must be higher than y1 ({})'.format(line, y2, y1)) 106 | 107 | if y2_int == y1_int: 108 | warnings.warn('filtering line {}: rounding y2 ({}) and y1 ({}) makes them equal'.format(line, y2, y1)) 109 | continue 110 | 111 | if x2_int == x1_int: 112 | warnings.warn('filtering line {}: rounding x2 ({}) and x1 ({}) makes them equal'.format(line, x2, x1)) 113 | continue 114 | 115 | img_id = row['ImageID'] 116 | annotation = {'cls_id': cls_id, 'x1': x1, 'x2': x2, 'y1': y1, 'y2': y2} 117 | 118 | if img_id in id_annotations: 119 | annotations = id_annotations[img_id] 120 | annotations['boxes'].append(annotation) 121 | else: 122 | id_annotations[img_id] = {'w': width, 'h': height, 'boxes': [annotation]} 123 | return id_annotations 124 | 125 | 126 | class OpenImagesGenerator(Generator): 127 | def __init__( 128 | self, main_dir, subset, version='2017_11', 129 | labels_filter=None, annotation_cache_dir='.', 130 | fixed_labels=False, 131 | **kwargs 132 | ): 133 | self.base_dir = os.path.join(main_dir, 'images', subset) 134 | metadata_dir = os.path.join(main_dir, version) 135 | annotation_cache_json = os.path.join(annotation_cache_dir, subset + '.json') 136 | 137 | self.id_to_labels, cls_index = get_labels(metadata_dir) 138 | 139 | if os.path.exists(annotation_cache_json): 140 | with open(annotation_cache_json, 'r') as f: 141 | self.annotations = json.loads(f.read()) 142 | else: 143 | self.annotations = generate_images_annotations_json(main_dir, metadata_dir, subset, cls_index) 144 | json.dump(self.annotations, open(annotation_cache_json, "w")) 145 | 146 | if labels_filter is not None: 147 | self.id_to_labels, self.annotations = self.__filter_data(labels_filter, fixed_labels) 148 | 149 | self.id_to_image_id = dict() 150 | for i, k in enumerate(self.annotations): 151 | self.id_to_image_id[i] = k 152 | 153 | super(OpenImagesGenerator, self).__init__(**kwargs) 154 | 155 | def __filter_data(self, labels_filter, fixed_labels): 156 | """ 157 | If you want to work with a subset of the labels just set a list with trainable labels 158 | :param labels_filter: Ex: labels_filter = ['Helmet', 'Hat', 'Analog television'] 159 | :param fixed_labels: If fixed_labels is true this will bring you the 'Helmet' label 160 | but also: 'bicycle helmet', 'welding helmet', 'ski helmet' etc... 161 | :return: 162 | """ 163 | 164 | labels_to_id = dict([(l, i) for i, l in enumerate(labels_filter)]) 165 | 166 | sub_labels_to_id = {} 167 | if fixed_labels: 168 | # there is/are no other sublabel(s) other than the labels itself 169 | sub_labels_to_id = labels_to_id 170 | else: 171 | for l in labels_filter: 172 | label = str.lower(l) 173 | for v in [v for v in self.id_to_labels.values() if label in str.lower(v)]: 174 | sub_labels_to_id[v] = labels_to_id[l] 175 | 176 | filtered_annotations = {} 177 | for k in self.annotations: 178 | img_ann = self.annotations[k] 179 | 180 | filtered_boxes = [] 181 | for ann in img_ann['boxes']: 182 | cls_id = ann['cls_id'] 183 | label = self.id_to_labels[cls_id] 184 | if label in sub_labels_to_id: 185 | ann['cls_id'] = sub_labels_to_id[label] 186 | filtered_boxes.append(ann) 187 | 188 | if len(filtered_boxes) > 0: 189 | filtered_annotations[k] = {'w': img_ann['w'], 'h': img_ann['h'], 'boxes': filtered_boxes} 190 | 191 | id_to_labels = dict([(labels_to_id[k], k) for k in labels_to_id]) 192 | return id_to_labels, filtered_annotations 193 | 194 | def size(self): 195 | return len(self.annotations) 196 | 197 | def num_classes(self): 198 | return len(self.id_to_labels) 199 | 200 | def name_to_label(self, name): 201 | raise NotImplementedError() 202 | 203 | def label_to_name(self, label): 204 | return self.id_to_labels[label] 205 | 206 | def image_aspect_ratio(self, image_index): 207 | img_annotations = self.annotations[self.id_to_image_id[image_index]] 208 | height, width = img_annotations['h'], img_annotations['w'] 209 | return float(width) / float(height) 210 | 211 | def image_path(self, image_index): 212 | path = os.path.join(self.base_dir, self.id_to_image_id[image_index] + '.jpg') 213 | return path 214 | 215 | def load_image(self, image_index): 216 | return read_image_bgr(self.image_path(image_index)) 217 | 218 | def load_annotations(self, image_index): 219 | image_annotations = self.annotations[self.id_to_image_id[image_index]] 220 | 221 | labels = image_annotations['boxes'] 222 | height, width = image_annotations['h'], image_annotations['w'] 223 | 224 | boxes = np.zeros((len(labels), 5)) 225 | for idx, ann in enumerate(labels): 226 | cls_id = ann['cls_id'] 227 | x1 = ann['x1'] * width 228 | x2 = ann['x2'] * width 229 | y1 = ann['y1'] * height 230 | y2 = ann['y2'] * height 231 | 232 | boxes[idx, 0] = x1 233 | boxes[idx, 1] = y1 234 | boxes[idx, 2] = x2 235 | boxes[idx, 3] = y2 236 | boxes[idx, 4] = cls_id 237 | 238 | return boxes 239 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/preprocessing/pascal_voc.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | from ..preprocessing.generator import Generator 18 | from ..utils.image import read_image_bgr 19 | 20 | import os 21 | import numpy as np 22 | from six import raise_from 23 | from PIL import Image 24 | 25 | try: 26 | import xml.etree.cElementTree as ET 27 | except ImportError: 28 | import xml.etree.ElementTree as ET 29 | 30 | voc_classes = { 31 | 'aeroplane' : 0, 32 | 'bicycle' : 1, 33 | 'bird' : 2, 34 | 'boat' : 3, 35 | 'bottle' : 4, 36 | 'bus' : 5, 37 | 'car' : 6, 38 | 'cat' : 7, 39 | 'chair' : 8, 40 | 'cow' : 9, 41 | 'diningtable' : 10, 42 | 'dog' : 11, 43 | 'horse' : 12, 44 | 'motorbike' : 13, 45 | 'person' : 14, 46 | 'pottedplant' : 15, 47 | 'sheep' : 16, 48 | 'sofa' : 17, 49 | 'train' : 18, 50 | 'tvmonitor' : 19 51 | } 52 | 53 | 54 | def _findNode(parent, name, debug_name = None, parse = None): 55 | if debug_name is None: 56 | debug_name = name 57 | 58 | result = parent.find(name) 59 | if result is None: 60 | raise ValueError('missing element \'{}\''.format(debug_name)) 61 | if parse is not None: 62 | try: 63 | return parse(result.text) 64 | except ValueError as e: 65 | raise_from(ValueError('illegal value for \'{}\': {}'.format(debug_name, e)), None) 66 | return result 67 | 68 | 69 | class PascalVocGenerator(Generator): 70 | def __init__( 71 | self, 72 | data_dir, 73 | set_name, 74 | classes=voc_classes, 75 | image_extension='.jpg', 76 | skip_truncated=False, 77 | skip_difficult=False, 78 | **kwargs 79 | ): 80 | self.data_dir = data_dir 81 | self.set_name = set_name 82 | self.classes = classes 83 | self.image_names = [l.strip().split(None, 1)[0] for l in open(os.path.join(data_dir, 'ImageSets', 'Main', set_name + '.txt')).readlines()] 84 | self.image_extension = image_extension 85 | self.skip_truncated = skip_truncated 86 | self.skip_difficult = skip_difficult 87 | 88 | self.labels = {} 89 | for key, value in self.classes.items(): 90 | self.labels[value] = key 91 | 92 | super(PascalVocGenerator, self).__init__(**kwargs) 93 | 94 | def size(self): 95 | return len(self.image_names) 96 | 97 | def num_classes(self): 98 | return len(self.classes) 99 | 100 | def name_to_label(self, name): 101 | return self.classes[name] 102 | 103 | def label_to_name(self, label): 104 | return self.labels[label] 105 | 106 | def image_aspect_ratio(self, image_index): 107 | path = os.path.join(self.data_dir, 'JPEGImages', self.image_names[image_index] + self.image_extension) 108 | image = Image.open(path) 109 | return float(image.width) / float(image.height) 110 | 111 | def load_image(self, image_index): 112 | path = os.path.join(self.data_dir, 'JPEGImages', self.image_names[image_index] + self.image_extension) 113 | return read_image_bgr(path) 114 | 115 | def __parse_annotation(self, element): 116 | truncated = _findNode(element, 'truncated', parse=int) 117 | difficult = _findNode(element, 'difficult', parse=int) 118 | 119 | class_name = _findNode(element, 'name').text 120 | if class_name not in self.classes: 121 | raise ValueError('class name \'{}\' not found in classes: {}'.format(class_name, list(self.classes.keys()))) 122 | 123 | box = np.zeros((1, 5)) 124 | box[0, 4] = self.name_to_label(class_name) 125 | 126 | bndbox = _findNode(element, 'bndbox') 127 | box[0, 0] = _findNode(bndbox, 'xmin', 'bndbox.xmin', parse=float) - 1 128 | box[0, 1] = _findNode(bndbox, 'ymin', 'bndbox.ymin', parse=float) - 1 129 | box[0, 2] = _findNode(bndbox, 'xmax', 'bndbox.xmax', parse=float) - 1 130 | box[0, 3] = _findNode(bndbox, 'ymax', 'bndbox.ymax', parse=float) - 1 131 | 132 | return truncated, difficult, box 133 | 134 | def __parse_annotations(self, xml_root): 135 | size_node = _findNode(xml_root, 'size') 136 | width = _findNode(size_node, 'width', 'size.width', parse=float) 137 | height = _findNode(size_node, 'height', 'size.height', parse=float) 138 | 139 | boxes = np.zeros((0, 5)) 140 | for i, element in enumerate(xml_root.iter('object')): 141 | try: 142 | truncated, difficult, box = self.__parse_annotation(element) 143 | except ValueError as e: 144 | raise_from(ValueError('could not parse object #{}: {}'.format(i, e)), None) 145 | 146 | if truncated and self.skip_truncated: 147 | continue 148 | if difficult and self.skip_difficult: 149 | continue 150 | boxes = np.append(boxes, box, axis=0) 151 | 152 | return boxes 153 | 154 | def load_annotations(self, image_index): 155 | filename = self.image_names[image_index] + '.xml' 156 | try: 157 | tree = ET.parse(os.path.join(self.data_dir, 'Annotations', filename)) 158 | return self.__parse_annotations(tree.getroot()) 159 | except ET.ParseError as e: 160 | raise_from(ValueError('invalid annotations file: {}: {}'.format(filename, e)), None) 161 | except ValueError as e: 162 | raise_from(ValueError('invalid annotations file: {}: {}'.format(filename, e)), None) 163 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/utils/__init__.py -------------------------------------------------------------------------------- /Detection/keras_retinanet/utils/__pycache__/__init__.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/utils/__pycache__/__init__.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/utils/__pycache__/anchors.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/utils/__pycache__/anchors.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/utils/__pycache__/colors.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/utils/__pycache__/colors.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/utils/__pycache__/image.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/utils/__pycache__/image.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/utils/__pycache__/transform.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/utils/__pycache__/transform.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/utils/__pycache__/visualization.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Detection/keras_retinanet/utils/__pycache__/visualization.cpython-35.pyc -------------------------------------------------------------------------------- /Detection/keras_retinanet/utils/anchors.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import numpy as np 18 | 19 | 20 | def anchor_targets_bbox( 21 | image_shape, 22 | annotations, 23 | num_classes, 24 | mask_shape=None, 25 | negative_overlap=0.4, 26 | positive_overlap=0.5, 27 | **kwargs 28 | ): 29 | anchors = anchors_for_shape(image_shape, **kwargs) 30 | 31 | # label: 1 is positive, 0 is negative, -1 is dont care 32 | labels = np.ones((anchors.shape[0], num_classes)) * -1 33 | 34 | if annotations.shape[0]: 35 | # obtain indices of gt annotations with the greatest overlap 36 | overlaps = compute_overlap(anchors, annotations[:, :4]) 37 | argmax_overlaps_inds = np.argmax(overlaps, axis=1) 38 | max_overlaps = overlaps[np.arange(overlaps.shape[0]), argmax_overlaps_inds] 39 | 40 | # assign bg labels first so that positive labels can clobber them 41 | labels[max_overlaps < negative_overlap, :] = 0 42 | 43 | # compute box regression targets 44 | annotations = annotations[argmax_overlaps_inds] 45 | 46 | # fg label: above threshold IOU 47 | positive_indices = max_overlaps >= positive_overlap 48 | labels[positive_indices, :] = 0 49 | labels[positive_indices, annotations[positive_indices, 4].astype(int)] = 1 50 | else: 51 | # no annotations? then everything is background 52 | labels[:] = 0 53 | annotations = np.zeros_like(anchors) 54 | 55 | # ignore annotations outside of image 56 | mask_shape = image_shape if mask_shape is None else mask_shape 57 | anchors_centers = np.vstack([(anchors[:, 0] + anchors[:, 2]) / 2, (anchors[:, 1] + anchors[:, 3]) / 2]).T 58 | indices = np.logical_or(anchors_centers[:, 0] >= mask_shape[1], anchors_centers[:, 1] >= mask_shape[0]) 59 | labels[indices, :] = -1 60 | 61 | return labels, annotations, anchors 62 | 63 | 64 | def anchors_for_shape( 65 | image_shape, 66 | pyramid_levels=None, 67 | ratios=None, 68 | scales=None, 69 | strides=None, 70 | sizes=None 71 | ): 72 | if pyramid_levels is None: 73 | pyramid_levels = [3, 4, 5, 6, 7] 74 | if strides is None: 75 | strides = [2 ** x for x in pyramid_levels] 76 | if sizes is None: 77 | sizes = [2 ** (x + 2) for x in pyramid_levels] 78 | if ratios is None: 79 | ratios = np.array([0.5, 1, 2]) 80 | if scales is None: 81 | scales = np.array([2 ** 0, 2 ** (1.0 / 3.0), 2 ** (2.0 / 3.0)]) 82 | 83 | # skip the first two levels 84 | image_shape = np.array(image_shape[:2]) 85 | for i in range(pyramid_levels[0] - 1): 86 | image_shape = (image_shape + 1) // 2 87 | 88 | # compute anchors over all pyramid levels 89 | all_anchors = np.zeros((0, 4)) 90 | for idx, p in enumerate(pyramid_levels): 91 | image_shape = (image_shape + 1) // 2 92 | anchors = generate_anchors(base_size=sizes[idx], ratios=ratios, scales=scales) 93 | shifted_anchors = shift(image_shape, strides[idx], anchors) 94 | all_anchors = np.append(all_anchors, shifted_anchors, axis=0) 95 | 96 | return all_anchors 97 | 98 | 99 | def shift(shape, stride, anchors): 100 | shift_x = (np.arange(0, shape[1]) + 0.5) * stride 101 | shift_y = (np.arange(0, shape[0]) + 0.5) * stride 102 | 103 | shift_x, shift_y = np.meshgrid(shift_x, shift_y) 104 | 105 | shifts = np.vstack(( 106 | shift_x.ravel(), shift_y.ravel(), 107 | shift_x.ravel(), shift_y.ravel() 108 | )).transpose() 109 | 110 | # add A anchors (1, A, 4) to 111 | # cell K shifts (K, 1, 4) to get 112 | # shift anchors (K, A, 4) 113 | # reshape to (K*A, 4) shifted anchors 114 | A = anchors.shape[0] 115 | K = shifts.shape[0] 116 | all_anchors = (anchors.reshape((1, A, 4)) + shifts.reshape((1, K, 4)).transpose((1, 0, 2))) 117 | all_anchors = all_anchors.reshape((K * A, 4)) 118 | 119 | return all_anchors 120 | 121 | 122 | def generate_anchors(base_size=16, ratios=None, scales=None): 123 | """ 124 | Generate anchor (reference) windows by enumerating aspect ratios X 125 | scales w.r.t. a reference window. 126 | """ 127 | 128 | if ratios is None: 129 | ratios = np.array([0.5, 1, 2]) 130 | 131 | if scales is None: 132 | scales = np.array([2 ** 0, 2 ** (1.0 / 3.0), 2 ** (2.0 / 3.0)]) 133 | 134 | num_anchors = len(ratios) * len(scales) 135 | 136 | # initialize output anchors 137 | anchors = np.zeros((num_anchors, 4)) 138 | 139 | # scale base_size 140 | anchors[:, 2:] = base_size * np.tile(scales, (2, len(ratios))).T 141 | 142 | # compute areas of anchors 143 | areas = anchors[:, 2] * anchors[:, 3] 144 | 145 | # correct for ratios 146 | anchors[:, 2] = np.sqrt(areas / np.repeat(ratios, len(scales))) 147 | anchors[:, 3] = anchors[:, 2] * np.repeat(ratios, len(scales)) 148 | 149 | # transform from (x_ctr, y_ctr, w, h) -> (x1, y1, x2, y2) 150 | anchors[:, 0::2] -= np.tile(anchors[:, 2] * 0.5, (2, 1)).T 151 | anchors[:, 1::2] -= np.tile(anchors[:, 3] * 0.5, (2, 1)).T 152 | 153 | return anchors 154 | 155 | 156 | def bbox_transform(anchors, gt_boxes, mean=None, std=None): 157 | """Compute bounding-box regression targets for an image.""" 158 | 159 | if mean is None: 160 | mean = np.array([0, 0, 0, 0]) 161 | if std is None: 162 | std = np.array([0.1, 0.1, 0.2, 0.2]) 163 | 164 | if isinstance(mean, (list, tuple)): 165 | mean = np.array(mean) 166 | elif not isinstance(mean, np.ndarray): 167 | raise ValueError('Expected mean to be a np.ndarray, list or tuple. Received: {}'.format(type(mean))) 168 | 169 | if isinstance(std, (list, tuple)): 170 | std = np.array(std) 171 | elif not isinstance(std, np.ndarray): 172 | raise ValueError('Expected std to be a np.ndarray, list or tuple. Received: {}'.format(type(std))) 173 | 174 | anchor_widths = anchors[:, 2] - anchors[:, 0] 175 | anchor_heights = anchors[:, 3] - anchors[:, 1] 176 | anchor_ctr_x = anchors[:, 0] + 0.5 * anchor_widths 177 | anchor_ctr_y = anchors[:, 1] + 0.5 * anchor_heights 178 | 179 | gt_widths = gt_boxes[:, 2] - gt_boxes[:, 0] 180 | gt_heights = gt_boxes[:, 3] - gt_boxes[:, 1] 181 | gt_ctr_x = gt_boxes[:, 0] + 0.5 * gt_widths 182 | gt_ctr_y = gt_boxes[:, 1] + 0.5 * gt_heights 183 | 184 | # clip widths to 1 185 | gt_widths = np.maximum(gt_widths, 1) 186 | gt_heights = np.maximum(gt_heights, 1) 187 | 188 | targets_dx = (gt_ctr_x - anchor_ctr_x) / anchor_widths 189 | targets_dy = (gt_ctr_y - anchor_ctr_y) / anchor_heights 190 | targets_dw = np.log(gt_widths / anchor_widths) 191 | targets_dh = np.log(gt_heights / anchor_heights) 192 | 193 | targets = np.stack((targets_dx, targets_dy, targets_dw, targets_dh)) 194 | targets = targets.T 195 | 196 | targets = (targets - mean) / std 197 | 198 | return targets 199 | 200 | 201 | def compute_overlap(a, b): 202 | """ 203 | Parameters 204 | ---------- 205 | a: (N, 4) ndarray of float 206 | b: (K, 4) ndarray of float 207 | Returns 208 | ------- 209 | overlaps: (N, K) ndarray of overlap between boxes and query_boxes 210 | """ 211 | area = (b[:, 2] - b[:, 0]) * (b[:, 3] - b[:, 1]) 212 | 213 | iw = np.minimum(np.expand_dims(a[:, 2], axis=1), b[:, 2]) - np.maximum(np.expand_dims(a[:, 0], 1), b[:, 0]) 214 | ih = np.minimum(np.expand_dims(a[:, 3], axis=1), b[:, 3]) - np.maximum(np.expand_dims(a[:, 1], 1), b[:, 1]) 215 | 216 | iw = np.maximum(iw, 0) 217 | ih = np.maximum(ih, 0) 218 | 219 | ua = np.expand_dims((a[:, 2] - a[:, 0]) * (a[:, 3] - a[:, 1]), axis=1) + area - iw * ih 220 | 221 | ua = np.maximum(ua, np.finfo(float).eps) 222 | 223 | intersection = iw * ih 224 | 225 | return intersection / ua 226 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/utils/coco_eval.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | from __future__ import print_function 18 | 19 | from pycocotools.coco import COCO 20 | from pycocotools.cocoeval import COCOeval 21 | 22 | import numpy as np 23 | import json 24 | import os 25 | 26 | 27 | def evaluate_coco(generator, model, threshold=0.05): 28 | # start collecting results 29 | results = [] 30 | image_ids = [] 31 | for index in range(generator.size()): 32 | image = generator.load_image(index) 33 | image = generator.preprocess_image(image) 34 | image, scale = generator.resize_image(image) 35 | 36 | # run network 37 | _, _, detections = model.predict_on_batch(np.expand_dims(image, axis=0)) 38 | 39 | # clip to image shape 40 | detections[:, :, 0] = np.maximum(0, detections[:, :, 0]) 41 | detections[:, :, 1] = np.maximum(0, detections[:, :, 1]) 42 | detections[:, :, 2] = np.minimum(image.shape[1], detections[:, :, 2]) 43 | detections[:, :, 3] = np.minimum(image.shape[0], detections[:, :, 3]) 44 | 45 | # correct boxes for image scale 46 | detections[0, :, :4] /= scale 47 | 48 | # change to (x, y, w, h) (MS COCO standard) 49 | detections[:, :, 2] -= detections[:, :, 0] 50 | detections[:, :, 3] -= detections[:, :, 1] 51 | 52 | # compute predicted labels and scores 53 | for i, j in np.transpose(np.where(detections[0, :, 4:] > threshold)): 54 | # append detections for each positively labeled class 55 | image_result = { 56 | 'image_id' : generator.image_ids[index], 57 | 'category_id' : generator.label_to_coco_label(j), 58 | 'score' : float(detections[0, i, 4 + j]), 59 | 'bbox' : (detections[0, i, :4]).tolist(), 60 | } 61 | 62 | # append detection to results 63 | results.append(image_result) 64 | 65 | # append image to list of processed images 66 | image_ids.append(generator.image_ids[index]) 67 | 68 | # print progress 69 | print('{}/{}'.format(index, generator.size()), end='\r') 70 | 71 | if not len(results): 72 | return 73 | 74 | # write output 75 | json.dump(results, open('{}_bbox_results.json'.format(generator.set_name), 'w'), indent=4) 76 | json.dump(image_ids, open('{}_processed_image_ids.json'.format(generator.set_name), 'w'), indent=4) 77 | 78 | # load results in COCO evaluation tool 79 | coco_true = generator.coco 80 | coco_pred = coco_true.loadRes('{}_bbox_results.json'.format(generator.set_name)) 81 | 82 | # run COCO evaluation 83 | coco_eval = COCOeval(coco_true, coco_pred, 'bbox') 84 | coco_eval.params.imgIds = image_ids 85 | coco_eval.evaluate() 86 | coco_eval.accumulate() 87 | coco_eval.summarize() 88 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/utils/colors.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | 3 | 4 | def label_color(label): 5 | """ Return a color from a set of predefined colors. Contains 80 colors in total. 6 | 7 | Args 8 | label: The label to get the color for. 9 | 10 | Returns 11 | A list of three values representing a RGB color. 12 | 13 | If no color is defined for a certain label, the color green is returned and a warning is printed. 14 | """ 15 | if label < len(colors): 16 | return colors[label] 17 | else: 18 | warnings.warn('Label {} has no color, returning default.'.format(label)) 19 | return (0, 255, 0) 20 | 21 | """ 22 | Generated using: 23 | 24 | ``` 25 | colors = [list((matplotlib.colors.hsv_to_rgb([x, 1.0, 1.0]) * 255).astype(int)) for x in np.arange(0, 1, 1.0 / 80)] 26 | shuffle(colors) 27 | pprint(colors) 28 | ``` 29 | """ 30 | colors = [ 31 | [31 , 0 , 255] , 32 | [0 , 159 , 255] , 33 | [255 , 95 , 0] , 34 | [255 , 19 , 0] , 35 | [255 , 0 , 0] , 36 | [255 , 38 , 0] , 37 | [0 , 255 , 25] , 38 | [255 , 0 , 133] , 39 | [255 , 172 , 0] , 40 | [108 , 0 , 255] , 41 | [0 , 82 , 255] , 42 | [0 , 255 , 6] , 43 | [255 , 0 , 152] , 44 | [223 , 0 , 255] , 45 | [12 , 0 , 255] , 46 | [0 , 255 , 178] , 47 | [108 , 255 , 0] , 48 | [184 , 0 , 255] , 49 | [255 , 0 , 76] , 50 | [146 , 255 , 0] , 51 | [51 , 0 , 255] , 52 | [0 , 197 , 255] , 53 | [255 , 248 , 0] , 54 | [255 , 0 , 19] , 55 | [255 , 0 , 38] , 56 | [89 , 255 , 0] , 57 | [127 , 255 , 0] , 58 | [255 , 153 , 0] , 59 | [0 , 255 , 255] , 60 | [0 , 255 , 216] , 61 | [0 , 255 , 121] , 62 | [255 , 0 , 248] , 63 | [70 , 0 , 255] , 64 | [0 , 255 , 159] , 65 | [0 , 216 , 255] , 66 | [0 , 6 , 255] , 67 | [0 , 63 , 255] , 68 | [31 , 255 , 0] , 69 | [255 , 57 , 0] , 70 | [255 , 0 , 210] , 71 | [0 , 255 , 102] , 72 | [242 , 255 , 0] , 73 | [255 , 191 , 0] , 74 | [0 , 255 , 63] , 75 | [255 , 0 , 95] , 76 | [146 , 0 , 255] , 77 | [184 , 255 , 0] , 78 | [255 , 114 , 0] , 79 | [0 , 255 , 235] , 80 | [255 , 229 , 0] , 81 | [0 , 178 , 255] , 82 | [255 , 0 , 114] , 83 | [255 , 0 , 57] , 84 | [0 , 140 , 255] , 85 | [0 , 121 , 255] , 86 | [12 , 255 , 0] , 87 | [255 , 210 , 0] , 88 | [0 , 255 , 44] , 89 | [165 , 255 , 0] , 90 | [0 , 25 , 255] , 91 | [0 , 255 , 140] , 92 | [0 , 101 , 255] , 93 | [0 , 255 , 82] , 94 | [223 , 255 , 0] , 95 | [242 , 0 , 255] , 96 | [89 , 0 , 255] , 97 | [165 , 0 , 255] , 98 | [70 , 255 , 0] , 99 | [255 , 0 , 172] , 100 | [255 , 76 , 0] , 101 | [203 , 255 , 0] , 102 | [204 , 0 , 255] , 103 | [255 , 0 , 229] , 104 | [255 , 133 , 0] , 105 | [127 , 0 , 255] , 106 | [0 , 235 , 255] , 107 | [0 , 255 , 197] , 108 | [255 , 0 , 191] , 109 | [0 , 44 , 255] , 110 | [50 , 255 , 0] 111 | ] 112 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/utils/eval.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | from __future__ import print_function 18 | 19 | from .anchors import compute_overlap 20 | from .visualization import draw_detections, draw_annotations 21 | 22 | import numpy as np 23 | import os 24 | 25 | import cv2 26 | import pickle 27 | 28 | 29 | def _compute_ap(recall, precision): 30 | """ Compute the average precision, given the recall and precision curves. 31 | 32 | Code originally from https://github.com/rbgirshick/py-faster-rcnn. 33 | 34 | # Arguments 35 | recall: The recall curve (list). 36 | precision: The precision curve (list). 37 | # Returns 38 | The average precision as computed in py-faster-rcnn. 39 | """ 40 | # correct AP calculation 41 | # first append sentinel values at the end 42 | mrec = np.concatenate(([0.], recall, [1.])) 43 | mpre = np.concatenate(([0.], precision, [0.])) 44 | 45 | # compute the precision envelope 46 | for i in range(mpre.size - 1, 0, -1): 47 | mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i]) 48 | 49 | # to calculate area under PR curve, look for points 50 | # where X axis (recall) changes value 51 | i = np.where(mrec[1:] != mrec[:-1])[0] 52 | 53 | # and sum (\Delta recall) * prec 54 | ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1]) 55 | return ap 56 | 57 | 58 | def _get_detections(generator, model, score_threshold=0.05, max_detections=100, save_path=None): 59 | """ Get the detections from the model using the generator. 60 | 61 | The result is a list of lists such that the size is: 62 | all_detections[num_images][num_classes] = detections[num_detections, 4 + num_classes] 63 | 64 | # Arguments 65 | generator : The generator used to run images through the model. 66 | model : The model to run on the images. 67 | score_threshold : The score confidence threshold to use. 68 | max_detections : The maximum number of detections to use per image. 69 | save_path : The path to save the images with visualized detections to. 70 | # Returns 71 | A list of lists containing the detections for each image in the generator. 72 | """ 73 | all_detections = [[None for i in range(generator.num_classes())] for j in range(generator.size())] 74 | 75 | for i in range(generator.size()): 76 | raw_image = generator.load_image(i) 77 | image = generator.preprocess_image(raw_image.copy()) 78 | image, scale = generator.resize_image(image) 79 | 80 | # run network 81 | _, _, detections = model.predict_on_batch(np.expand_dims(image, axis=0)) 82 | 83 | # clip to image shape 84 | detections[:, :, 0] = np.maximum(0, detections[:, :, 0]) 85 | detections[:, :, 1] = np.maximum(0, detections[:, :, 1]) 86 | detections[:, :, 2] = np.minimum(image.shape[1], detections[:, :, 2]) 87 | detections[:, :, 3] = np.minimum(image.shape[0], detections[:, :, 3]) 88 | 89 | # correct boxes for image scale 90 | detections[0, :, :4] /= scale 91 | 92 | # select scores from detections 93 | scores = detections[0, :, 4:] 94 | 95 | # select indices which have a score above the threshold 96 | indices = np.where(detections[0, :, 4:] > score_threshold) 97 | 98 | # select those scores 99 | scores = scores[indices] 100 | 101 | # find the order with which to sort the scores 102 | scores_sort = np.argsort(-scores)[:max_detections] 103 | 104 | # select detections 105 | image_boxes = detections[0, indices[0][scores_sort], :4] 106 | image_scores = np.expand_dims(detections[0, indices[0][scores_sort], 4 + indices[1][scores_sort]], axis=1) 107 | image_detections = np.append(image_boxes, image_scores, axis=1) 108 | image_predicted_labels = indices[1][scores_sort] 109 | 110 | if save_path is not None: 111 | draw_annotations(raw_image, generator.load_annotations(i), generator=generator) 112 | draw_detections(raw_image, detections[0, indices[0][scores_sort], :], generator=generator) 113 | 114 | cv2.imwrite(os.path.join(save_path, '{}.png'.format(i)), raw_image) 115 | 116 | # copy detections to all_detections 117 | for label in range(generator.num_classes()): 118 | all_detections[i][label] = image_detections[image_predicted_labels == label, :] 119 | 120 | print('{}/{}'.format(i, generator.size()), end='\r') 121 | 122 | return all_detections 123 | 124 | 125 | def _get_annotations(generator): 126 | """ Get the ground truth annotations from the generator. 127 | 128 | The result is a list of lists such that the size is: 129 | all_detections[num_images][num_classes] = annotations[num_detections, 5] 130 | 131 | # Arguments 132 | generator : The generator used to retrieve ground truth annotations. 133 | # Returns 134 | A list of lists containing the annotations for each image in the generator. 135 | """ 136 | all_annotations = [[None for i in range(generator.num_classes())] for j in range(generator.size())] 137 | 138 | for i in range(generator.size()): 139 | # load the annotations 140 | annotations = generator.load_annotations(i) 141 | 142 | # copy detections to all_annotations 143 | for label in range(generator.num_classes()): 144 | all_annotations[i][label] = annotations[annotations[:, 4] == label, :4].copy() 145 | 146 | print('{}/{}'.format(i, generator.size()), end='\r') 147 | 148 | return all_annotations 149 | 150 | 151 | def evaluate( 152 | generator, 153 | model, 154 | iou_threshold=0.5, 155 | score_threshold=0.05, 156 | max_detections=100, 157 | save_path=None 158 | ): 159 | """ Evaluate a given dataset using a given model. 160 | 161 | # Arguments 162 | generator : The generator that represents the dataset to evaluate. 163 | model : The model to evaluate. 164 | iou_threshold : The threshold used to consider when a detection is positive or negative. 165 | score_threshold : The score confidence threshold to use for detections. 166 | max_detections : The maximum number of detections to use per image. 167 | save_path : The path to save images with visualized detections to. 168 | # Returns 169 | A dict mapping class names to mAP scores. 170 | """ 171 | # gather all detections and annotations 172 | all_detections = _get_detections(generator, model, score_threshold=score_threshold, max_detections=max_detections, save_path=save_path) 173 | all_annotations = _get_annotations(generator) 174 | average_precisions = {} 175 | 176 | # all_detections = pickle.load(open('all_detections.pkl', 'rb')) 177 | # all_annotations = pickle.load(open('all_annotations.pkl', 'rb')) 178 | # pickle.dump(all_detections, open('all_detections.pkl', 'wb')) 179 | # pickle.dump(all_annotations, open('all_annotations.pkl', 'wb')) 180 | 181 | # process detections and annotations 182 | for label in range(generator.num_classes()): 183 | false_positives = np.zeros((0,)) 184 | true_positives = np.zeros((0,)) 185 | scores = np.zeros((0,)) 186 | num_annotations = 0.0 187 | 188 | for i in range(generator.size()): 189 | detections = all_detections[i][label] 190 | annotations = all_annotations[i][label] 191 | num_annotations += annotations.shape[0] 192 | detected_annotations = [] 193 | 194 | for d in detections: 195 | scores = np.append(scores, d[4]) 196 | 197 | if annotations.shape[0] == 0: 198 | false_positives = np.append(false_positives, 1) 199 | true_positives = np.append(true_positives, 0) 200 | continue 201 | 202 | overlaps = compute_overlap(np.expand_dims(d, axis=0), annotations) 203 | assigned_annotation = np.argmax(overlaps, axis=1) 204 | max_overlap = overlaps[0, assigned_annotation] 205 | 206 | if max_overlap >= iou_threshold and assigned_annotation not in detected_annotations: 207 | false_positives = np.append(false_positives, 0) 208 | true_positives = np.append(true_positives, 1) 209 | detected_annotations.append(assigned_annotation) 210 | else: 211 | false_positives = np.append(false_positives, 1) 212 | true_positives = np.append(true_positives, 0) 213 | 214 | # no annotations -> AP for this class is 0 (is this correct?) 215 | if num_annotations == 0: 216 | average_precisions[label] = 0 217 | continue 218 | 219 | # sort by score 220 | indices = np.argsort(-scores) 221 | false_positives = false_positives[indices] 222 | true_positives = true_positives[indices] 223 | 224 | # compute false positives and true positives 225 | false_positives = np.cumsum(false_positives) 226 | true_positives = np.cumsum(true_positives) 227 | 228 | # compute recall and precision 229 | recall = true_positives / num_annotations 230 | precision = true_positives / np.maximum(true_positives + false_positives, np.finfo(np.float64).eps) 231 | 232 | # compute average precision 233 | average_precision = _compute_ap(recall, precision) 234 | average_precisions[label] = average_precision 235 | 236 | return average_precisions 237 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/utils/image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | from __future__ import division 18 | import keras 19 | import time 20 | import numpy as np 21 | import scipy.ndimage as ndi 22 | import cv2 23 | from PIL import Image 24 | 25 | from .transform import change_transform_origin, transform_aabb 26 | 27 | 28 | def read_image_bgr(path): 29 | image = np.asarray(Image.open(path).convert('RGB')) 30 | return image[:, :, ::-1].copy() 31 | 32 | def read_image_array(image_array): 33 | image = np.asarray(Image.fromarray(np.uint8(image_array))) 34 | return image[:, :, ::-1].copy() 35 | 36 | def read_image_stream(image_stream): 37 | image = np.asarray(Image.open(image_stream)) 38 | return image[:, :, ::-1].copy() 39 | 40 | 41 | def preprocess_image(x): 42 | # mostly identical to "https://github.com/fchollet/keras/blob/master/keras/applications/imagenet_utils.py" 43 | # except for converting RGB -> BGR since we assume BGR already 44 | x = x.astype(keras.backend.floatx()) 45 | if keras.backend.image_data_format() == 'channels_first': 46 | if x.ndim == 3: 47 | x[0, :, :] -= 103.939 48 | x[1, :, :] -= 116.779 49 | x[2, :, :] -= 123.68 50 | else: 51 | x[:, 0, :, :] -= 103.939 52 | x[:, 1, :, :] -= 116.779 53 | x[:, 2, :, :] -= 123.68 54 | else: 55 | x[..., 0] -= 103.939 56 | x[..., 1] -= 116.779 57 | x[..., 2] -= 123.68 58 | 59 | return x 60 | 61 | 62 | def adjust_transform_for_image(transform, image, relative_translation): 63 | """ Adjust a transformation for a specific image. 64 | 65 | The translation of the matrix will be scaled with the size of the image. 66 | The linear part of the transformation will adjusted so that the origin of the transformation will be at the center of the image. 67 | """ 68 | height, width, channels = image.shape 69 | 70 | result = transform 71 | 72 | # Scale the translation with the image size if specified. 73 | if relative_translation: 74 | result[0:2, 2] *= [width, height] 75 | 76 | # Move the origin of transformation. 77 | result = change_transform_origin(transform, (0.5 * width, 0.5 * height)) 78 | 79 | return result 80 | 81 | 82 | class TransformParameters: 83 | """ Struct holding parameters determining how to apply a transformation to an image. 84 | 85 | # Arguments 86 | fill_mode: One of: 'constant', 'nearest', 'reflect', 'wrap' 87 | interpolation: One of: 'nearest', 'linear', 'cubic', 'area', 'lanczos4' 88 | cval: Fill value to use with fill_mode='constant' 89 | data_format: Same as for keras.preprocessing.image.apply_transform 90 | relative_translation: If true (the default), interpret translation as a factor of the image size. 91 | If false, interpret it as absolute pixels. 92 | """ 93 | def __init__( 94 | self, 95 | fill_mode = 'nearest', 96 | interpolation = 'linear', 97 | cval = 0, 98 | data_format = None, 99 | relative_translation = True, 100 | ): 101 | self.fill_mode = fill_mode 102 | self.cval = cval 103 | self.interpolation = interpolation 104 | self.relative_translation = relative_translation 105 | 106 | if data_format is None: 107 | data_format = keras.backend.image_data_format() 108 | self.data_format = data_format 109 | 110 | if data_format == 'channels_first': 111 | self.channel_axis = 0 112 | elif data_format == 'channels_last': 113 | self.channel_axis = 2 114 | else: 115 | raise ValueError("invalid data_format, expected 'channels_first' or 'channels_last', got '{}'".format(data_format)) 116 | 117 | def cvBorderMode(self): 118 | if self.fill_mode == 'constant': 119 | return cv2.BORDER_CONSTANT 120 | if self.fill_mode == 'nearest': 121 | return cv2.BORDER_REPLICATE 122 | if self.fill_mode == 'reflect': 123 | return cv2.BORDER_REFLECT_101 124 | if self.fill_mode == 'wrap': 125 | return cv2.BORDER_WRAP 126 | 127 | def cvInterpolation(self): 128 | if self.interpolation == 'nearest': 129 | return cv2.INTER_NEAREST 130 | if self.interpolation == 'linear': 131 | return cv2.INTER_LINEAR 132 | if self.interpolation == 'cubic': 133 | return cv2.INTER_CUBIC 134 | if self.interpolation == 'area': 135 | return cv2.INTER_AREA 136 | if self.interpolation == 'lanczos4': 137 | return cv2.INTER_LANCZOS4 138 | 139 | 140 | def apply_transform(matrix, image, params): 141 | """ 142 | Apply a transformation to an image. 143 | 144 | The origin of transformation is at the top left corner of the image. 145 | 146 | The matrix is interpreted such that a point (x, y) on the original image is moved to transform * (x, y) in the generated image. 147 | Mathematically speaking, that means that the matrix is a transformation from the transformed image space to the original image space. 148 | 149 | Parameters: 150 | matrix: A homogenous 3 by 3 matrix holding representing the transformation to apply. 151 | image: The image to transform. 152 | params: The transform parameters (see TransformParameters) 153 | """ 154 | if params.channel_axis != 2: 155 | image = np.moveaxis(image, params.channel_axis, 2) 156 | 157 | output = cv2.warpAffine( 158 | image, 159 | matrix[:2, :], 160 | dsize = (image.shape[1], image.shape[0]), 161 | flags = params.cvInterpolation(), 162 | borderMode = params.cvBorderMode(), 163 | borderValue = params.cval, 164 | ) 165 | 166 | if params.channel_axis != 2: 167 | output = np.moveaxis(output, 2, params.channel_axis) 168 | return output 169 | 170 | 171 | def resize_image(img, min_side=800, max_side=1333): 172 | (rows, cols, _) = img.shape 173 | 174 | smallest_side = min(rows, cols) 175 | 176 | # rescale the image so the smallest side is min_side 177 | scale = min_side / smallest_side 178 | 179 | # check if the largest side is now greater than max_side, wich can happen 180 | # when images have a large aspect ratio 181 | largest_side = max(rows, cols) 182 | if largest_side * scale > max_side: 183 | scale = max_side / largest_side 184 | 185 | # resize the image with the computed scale 186 | img = cv2.resize(img, None, fx=scale, fy=scale) 187 | 188 | return img, scale 189 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/utils/keras_version.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | from __future__ import print_function 18 | 19 | import keras 20 | import sys 21 | 22 | minimum_keras_version = 2, 1, 3 23 | 24 | 25 | def keras_version(): 26 | return tuple(map(int, keras.__version__.split('.'))) 27 | 28 | 29 | def keras_version_ok(): 30 | return keras_version() >= minimum_keras_version 31 | 32 | 33 | def assert_keras_version(): 34 | detected = keras.__version__ 35 | required = '.'.join(map(str, minimum_keras_version)) 36 | assert(keras_version() >= minimum_keras_version), 'You are using keras version {}. The minimum required version is {}.'.format(detected, required) 37 | 38 | 39 | def check_keras_version(): 40 | try: 41 | assert_keras_version() 42 | except AssertionError as e: 43 | print(e, file=sys.stderr) 44 | sys.exit(1) 45 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/utils/model.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | 18 | def freeze(model): 19 | """ Set all layers in a model to non-trainable. 20 | 21 | The weights for these layers will not be updated during training. 22 | 23 | This function modifies the given model in-place, 24 | but it also returns the modified model to allow easy chaining with other functions. 25 | """ 26 | for layer in model.layers: 27 | layer.trainable = False 28 | return model 29 | -------------------------------------------------------------------------------- /Detection/keras_retinanet/utils/visualization.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2017-2018 Fizyr (https://fizyr.com) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | """ 16 | 17 | import cv2 18 | import numpy as np 19 | 20 | from .colors import label_color 21 | 22 | 23 | def draw_box(image, box, color, thickness=2): 24 | """ Draws a box on an image with a given color. 25 | 26 | # Arguments 27 | image : The image to draw on. 28 | box : A list of 4 elements (x1, y1, x2, y2). 29 | color : The color of the box. 30 | thickness : The thickness of the lines to draw a box with. 31 | """ 32 | b = np.array(box).astype(int) 33 | cv2.rectangle(image, (b[0], b[1]), (b[2], b[3]), color, thickness, cv2.LINE_AA) 34 | 35 | 36 | def draw_caption(image, box, caption): 37 | """ Draws a caption above the box in an image. 38 | 39 | # Arguments 40 | image : The image to draw on. 41 | box : A list of 4 elements (x1, y1, x2, y2). 42 | caption : String containing the text to draw. 43 | """ 44 | b = np.array(box).astype(int) 45 | cv2.putText(image, caption, (b[0], b[1] - 10), cv2.FONT_HERSHEY_PLAIN, 1, (0, 0, 0), 3) 46 | cv2.putText(image, caption, (b[0], b[1] - 10), cv2.FONT_HERSHEY_PLAIN, 1, (255, 255, 255), 2) 47 | 48 | 49 | def draw_boxes(image, boxes, color, thickness=2): 50 | """ Draws boxes on an image with a given color. 51 | 52 | # Arguments 53 | image : The image to draw on. 54 | boxes : A [N, 4] matrix (x1, y1, x2, y2). 55 | color : The color of the boxes. 56 | thickness : The thickness of the lines to draw boxes with. 57 | """ 58 | for b in boxes: 59 | draw_box(image, b, color, thickness=thickness) 60 | 61 | 62 | def draw_detections(image, detections, color=None, generator=None): 63 | """ Draws detections in an image. 64 | 65 | # Arguments 66 | image : The image to draw on. 67 | detections : A [N, 4 + num_classes] matrix (x1, y1, x2, y2, cls_1, cls_2, ...). 68 | color : The color of the boxes. By default the color from keras_retinanet.utils.colors.label_color will be used. 69 | generator : (optional) Generator which can map label to class name. 70 | """ 71 | for d in detections: 72 | label = np.argmax(d[4:]) 73 | c = color if color is not None else label_color(label) 74 | score = d[4 + label] 75 | caption = (generator.label_to_name(label) if generator else str(label)) + ': {0:.2f}'.format(score) 76 | draw_caption(image, d, caption) 77 | 78 | draw_box(image, d, color=c) 79 | 80 | 81 | def draw_annotations(image, annotations, color=(0, 255, 0), generator=None): 82 | """ Draws annotations in an image. 83 | 84 | # Arguments 85 | image : The image to draw on. 86 | annotations : A [N, 5] matrix (x1, y1, x2, y2, label). 87 | color : The color of the boxes. By default the color from keras_retinanet.utils.colors.label_color will be used. 88 | generator : (optional) Generator which can map label to class name. 89 | """ 90 | for a in annotations: 91 | label = a[4] 92 | c = color if color is not None else label_color(label) 93 | caption = '{}'.format(generator.label_to_name(label) if generator else label) 94 | draw_caption(image, a, caption) 95 | 96 | draw_box(image, a, color=c) 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | 3 | Copyright (c) 2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pothole_Detection.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/Pothole_Detection.gif -------------------------------------------------------------------------------- /Pothole_Train.py: -------------------------------------------------------------------------------- 1 | from imageai.Detection.Custom import DetectionModelTrainer 2 | 3 | trainer = DetectionModelTrainer() 4 | trainer.setModelTypeAsYOLOv3() 5 | trainer.setDataDirectory(data_directory="Pothole") 6 | trainer.setTrainConfig(object_names_array=["Pothole Severity Low","Pothole Severity High","Pothole Severity Medium"], batch_size=4, num_experiments=30, train_from_pretrained_model="pretrained-yolov3.h5") #download pre-trained model via https://github.com/OlafenwaMoses/ImageAI/releases/download/essential-v4/pretrained-yolov3.h5 7 | trainer.trainModel() 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Pothole_Video.py: -------------------------------------------------------------------------------- 1 | from imageai.Detection.Custom import CustomVideoObjectDetection 2 | import os 3 | 4 | execution_path = os.getcwd() 5 | 6 | video_detector = CustomVideoObjectDetection() 7 | video_detector.setModelTypeAsYOLOv3() 8 | video_detector.setModelPath("Pothole.h5") # download via https://github.com/OlafenwaMoses/ImageAI/releases/download/essential-v4/hololens-ex-60--loss-2.76.h5 9 | video_detector.setJsonPath("detection_config.json") # download via https://github.com/OlafenwaMoses/ImageAI/releases/download/essential-v4/detection_config.json 10 | video_detector.loadModel() 11 | 12 | video_detector.detectObjectsFromVideo(input_file_path="Video_Input", 13 | output_file_path=os.path.join(execution_path, "Pothole"), 14 | frames_per_second=20, 15 | minimum_percentage_probability=40, 16 | log_progress=True) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PotholeDetection 2 | Classification of Potholes Using Yolov3,Pytorch,Opencv,Python 3 | 4 | ---- 5 | This Model is train on 30k dataset each have 10k Data 6 | For each class (Low,Medium,High) 7 | 8 | ******************* 9 | Model:https://drive.google.com/open?id=1z1285aoruDwI8_6glDk2F1SiOLGedCb4 10 | -------------------------------------------------------------------------------- /detection_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "labels" : [ 3 | "Pothole Severity High", 4 | "Pothole Severity Low", 5 | "Pothole Severity Medium" 6 | ], 7 | "anchors" : [ 8 | [ 9 | 131, 10 | 134, 11 | 181, 12 | 80, 13 | 259, 14 | 214 15 | ], 16 | [ 17 | 67, 18 | 42, 19 | 71, 20 | 137, 21 | 92, 22 | 75 23 | ], 24 | [ 25 | 32, 26 | 31, 27 | 41, 28 | 45, 29 | 51, 30 | 83 31 | ] 32 | ] 33 | } -------------------------------------------------------------------------------- /imageai-2.0.2-py3-none-any.whlold: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiesh/PotholeDetection/9da053b591e5b3f7947e225a549caf849a0a0607/imageai-2.0.2-py3-none-any.whlold --------------------------------------------------------------------------------