├── models ├── __init__.py ├── backbone_models.py ├── retinanet_shared_heads.py └── resnetFPN.py ├── .dropbox ├── data ├── __init__.py ├── transforms.py └── detectionDatasets.py ├── samples ├── submission-testset.zip ├── submission-valset.zip ├── testset.log └── valset.log ├── .gitignore ├── modules ├── __init__.py ├── solver.py ├── anchor_box_retinanet.py ├── utils.py ├── evaluation.py ├── detection_loss.py └── box_utils.py ├── LICENSE ├── commands.txt ├── README.md ├── evaluate.py └── train.py /models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.dropbox: -------------------------------------------------------------------------------- 1 | {"tag": "shared", "ns": 6211252784} -------------------------------------------------------------------------------- /data/__init__.py: -------------------------------------------------------------------------------- 1 | from .detectionDatasets import DetectionDataset, custum_collate 2 | 3 | -------------------------------------------------------------------------------- /samples/submission-testset.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Viveksbawa/SARAS-ESAD-Baseline/HEAD/samples/submission-testset.zip -------------------------------------------------------------------------------- /samples/submission-valset.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Viveksbawa/SARAS-ESAD-Baseline/HEAD/samples/submission-valset.zip -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | slurm-* 4 | run_* 5 | # IPython Notebook 6 | .ipynb_checkpoints 7 | 8 | .vscode* 9 | # weights 10 | weights/ 11 | 12 | #DS_Store 13 | .DS_Store 14 | # pycharm 15 | .idea/ 16 | __pycache__/ 17 | 18 | #directories 19 | results/ 20 | -------------------------------------------------------------------------------- /modules/__init__.py: -------------------------------------------------------------------------------- 1 | class AverageMeter(object): 2 | """Computes and stores the average and current value""" 3 | def __init__(self, momentum=0.95): 4 | self.momentum = momentum 5 | self.reset() 6 | 7 | def reset(self): 8 | self.val = 0 9 | self.avg = 0 10 | self.count = 0 11 | 12 | def update(self, val, n=1): 13 | if n==0: 14 | n=1 15 | self.val = val 16 | if self.count == 0: 17 | self.avg = self.val 18 | else: 19 | self.avg = self.avg*self.momentum + (1-self.momentum)* val 20 | self.count += n 21 | -------------------------------------------------------------------------------- /models/backbone_models.py: -------------------------------------------------------------------------------- 1 | from .resnetFPN import resnetfpn 2 | import torch 3 | import pdb 4 | 5 | def backbone_models(modelname, model_dir, use_bias): 6 | 7 | if modelname[:6] == 'resnet': 8 | # print(modelname) 9 | modelperms = {'resnet18': [2, 2, 2, 2], 'resnet34': [3, 4, 6, 3], 'resnet50': [3, 4, 6, 3], 10 | 'resnet101': [3, 4, 23, 3], 'resnet152': [3, 8, 36, 3]} 11 | model = resnetfpn(modelperms[modelname], modelname, use_bias) 12 | # print('here here', model) 13 | if len(model_dir)>1: # load imagenet pretrained weights 14 | load_dict = torch.load(model_dir + modelname+'.pth') 15 | model.load_my_state_dict(load_dict) 16 | 17 | return model 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Gurkirt Singh 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. -------------------------------------------------------------------------------- /data/transforms.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torchvision.transforms import functional as F 3 | import math 4 | 5 | 6 | # modified from https://github.com/chengyangfu/retinamask/blob/master/maskrcnn_benchmark/structures/image_list.py 7 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 8 | def get_image_list_resized(tensors): 9 | max_size = tuple(max(s) for s in zip(*[img.shape for img in tensors])) 10 | stride = 32 11 | max_size = list(max_size) 12 | max_size[1] = int(math.ceil(max_size[1] / stride) * stride) 13 | max_size[2] = int(math.ceil(max_size[2] / stride) * stride) 14 | max_size = tuple(max_size) 15 | 16 | batch_shape = (len(tensors),) + max_size 17 | batched_imgs = tensors[0].new(*batch_shape).zero_() 18 | 19 | for img, pad_img in zip(tensors, batched_imgs): 20 | pad_img[: img.shape[0], : img.shape[1], : img.shape[2]].copy_(img) 21 | 22 | # image_sizes = [im.shape[-2:] for im in tensors] 23 | return batched_imgs 24 | 25 | 26 | # from https://github.com/chengyangfu/retinamask/blob/master/maskrcnn_benchmark/data/transforms/transforms.py 27 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 28 | class Resize(object): 29 | def __init__(self, min_size, max_size): 30 | self.min_size = min_size 31 | self.max_size = max_size 32 | 33 | # modified from torchvision to add support for max size 34 | def get_size(self, image_size): 35 | 36 | if self.min_size == self.max_size: 37 | 38 | return (self.min_size, self.max_size) 39 | 40 | else: 41 | w, h = image_size 42 | size = self.min_size 43 | max_size = self.max_size 44 | if max_size is not None: 45 | min_original_size = float(min((w, h))) 46 | max_original_size = float(max((w, h))) 47 | if max_original_size / min_original_size * size > max_size: 48 | size = int(round(max_size * min_original_size / max_original_size)) 49 | 50 | if (w <= h and w == size) or (h <= w and h == size): 51 | return (h, w) 52 | 53 | if w < h: 54 | ow = size 55 | oh = int(round(size * h / w)) 56 | else: 57 | oh = size 58 | ow = int(round(size * w / h)) 59 | 60 | return (oh, ow) 61 | 62 | def __call__(self, image): 63 | size = self.get_size(image.size) 64 | image = F.resize(image, size) 65 | return image 66 | 67 | 68 | -------------------------------------------------------------------------------- /modules/solver.py: -------------------------------------------------------------------------------- 1 | import torch, pdb 2 | import torch.optim as optim 3 | # from .madamw import Adam as AdamM 4 | # from .adamw import Adam as AdamW 5 | # from torch.optim.lr_scheduler import MultiStepLR 6 | 7 | class WarmupMultiStepLR(torch.optim.lr_scheduler._LRScheduler): 8 | def __init__(self, optimizer, milestones, gammas, last_epoch=-1): 9 | self.milestones = milestones 10 | self.gammas = gammas 11 | assert len(gammas) == len(milestones), 'Milestones and gammas should be of same length gammas are of len ' + (len(gammas)) + ' and milestones '+ str(len(milestones)) 12 | super(WarmupMultiStepLR, self).__init__(optimizer, last_epoch) 13 | 14 | def get_lr(self): 15 | if self.last_epoch not in self.milestones: 16 | return [group['lr'] for group in self.optimizer.param_groups] 17 | else: 18 | index = self.milestones.index(self.last_epoch) 19 | return [group['lr'] * self.gammas[index] for group in self.optimizer.param_groups] 20 | 21 | def print_lr(self): 22 | print([[group['name'], group['lr']] for group in self.optimizer.param_groups]) 23 | 24 | def get_optim(args, net): 25 | freeze_layers = ['backbone_net.layer'+str(n) for n in range(1, args.freezeupto+1)] 26 | params = [] 27 | solver_print_str = '\n\nSolver configs are as follow \n\n\n' 28 | for key, value in net.named_parameters(): 29 | 30 | if args.freezeupto>0 and (key.find('backbone_net.conv1')>-1 or key.find('backbone_net.bn1')>-1): # Freeze first conv layer and bn layer in resnet 31 | value.requires_grad = False 32 | continue 33 | 34 | if key.find('backbone_net')>-1: 35 | for layer_id in freeze_layers: 36 | if key.find(layer_id)>-1: 37 | value.requires_grad = False 38 | continue 39 | 40 | if not value.requires_grad: 41 | continue 42 | 43 | lr = args.lr 44 | wd = args.weight_decay 45 | 46 | if args.optim == 'ADAM': 47 | wd = 0.0 48 | 49 | if "bias" in key: 50 | lr = lr*2.0 51 | 52 | if args.optim == 'SGD': 53 | params += [{"params": [value], "name":key, "lr": lr, "weight_decay":wd, "momentum":args.momentum}] 54 | else: 55 | params += [{"params": [value], "name":key, "lr": lr, "weight_decay":wd}] 56 | 57 | print_l = key +' is trained at the rate of ' + str(lr) 58 | print(print_l) 59 | solver_print_str += print_l + '\n' 60 | 61 | 62 | if args.optim == 'SGD': 63 | optimizer = optim.SGD(params) 64 | elif args.optim == 'ADAM': 65 | optimizer = optim.Adam(params) 66 | # elif args.optim == 'ADAMW': 67 | # optimizer = AdamW(params) 68 | # elif args.optim == 'ADAMM': 69 | # optimizer = AdamM(params) 70 | else: 71 | error('Define optimiser type ') 72 | 73 | solver_print_str += 'optimizer is '+ args.optim + '\nDone solver configs\n\n' 74 | 75 | scheduler = WarmupMultiStepLR(optimizer, args.milestones, args.gammas) 76 | 77 | return optimizer, scheduler, solver_print_str -------------------------------------------------------------------------------- /modules/anchor_box_retinanet.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from math import sqrt as sqrt 3 | from itertools import product as product 4 | import numpy as np 5 | 6 | # from https://github.com/facebookresearch/maskrcnn-benchmark/blob/master/maskrcnn_benchmark/modeling/rpn/anchor_generator.py 7 | class BufferList(torch.nn.Module): 8 | """ 9 | 10 | Similar to nn.ParameterList, but for buffers 11 | 12 | """ 13 | 14 | def __init__(self, buffers=None): 15 | super(BufferList, self).__init__() 16 | if buffers is not None: 17 | self.extend(buffers) 18 | 19 | def extend(self, buffers): 20 | offset = len(self) 21 | for i, buffer in enumerate(buffers): 22 | self.register_buffer(str(offset + i), buffer) 23 | return self 24 | 25 | def __len__(self): 26 | return len(self._buffers) 27 | 28 | def __iter__(self): 29 | return iter(self._buffers.values()) 30 | 31 | 32 | class anchorBox(torch.nn.Module): 33 | """Compute anchorbox coordinates in center-offset form for each source 34 | feature map. 35 | """ 36 | def __init__(self, anchor_type = 'pdf9', sizes = [32, 64, 128, 256, 512], 37 | ratios = np.asarray([0.5, 1 / 1., 2.0]), 38 | strides = [8, 16, 32, 64, 128], 39 | scales = np.array([1, 1.25992, 1.58740])): 40 | 41 | super(anchorBox, self).__init__() 42 | self.sizes = sizes 43 | self.ratios = ratios 44 | self.scales = scales 45 | self.strides = strides 46 | if anchor_type != 'pdf9': 47 | self.scales = np.array([2 ** 0,]) 48 | self.ar = len(self.ratios)*len(self.ratios) 49 | self.cell_anchors = BufferList(self._get_cell_anchors()) 50 | 51 | def _get_cell_anchors(self): 52 | anchors = [] 53 | for s1 in self.sizes: 54 | p_anchors = np.asarray(self._gen_generate_anchors_on_one_level(s1)) 55 | p_anchors = torch.FloatTensor(p_anchors).cuda() 56 | anchors.append(p_anchors) 57 | 58 | return anchors 59 | 60 | # modified from https://github.com/fizyr/keras-retinanet/blob/master/keras_retinanet/utils/anchors.py 61 | # Copyright 2017-2018 Fizyr (https://fizyr.com) 62 | def _gen_generate_anchors_on_one_level(self, base_size=32): 63 | 64 | """ 65 | Generate anchor (reference) windows by enumerating aspect ratios X 66 | scales w.r.t. a reference window. 67 | 68 | """ 69 | 70 | num_anchors = len(self.ratios) * len(self.scales) 71 | 72 | # initialize output anchors 73 | anchors = np.zeros((num_anchors, 4)) 74 | 75 | # scale base_size 76 | anchors[:, 2:] = base_size * np.tile(self.scales, (2, len(self.ratios))).T 77 | 78 | # compute areas of anchors 79 | areas = anchors[:, 2] * anchors[:, 3] 80 | 81 | anchors[:, 2] = np.sqrt(areas / np.repeat(self.ratios, len(self.scales))) 82 | anchors[:, 3] = anchors[:, 2] * np.repeat(self.ratios, len(self.scales)) 83 | # transform from (x_ctr, y_ctr, w, h) -> (x1, y1, x2, y2) 84 | anchors[:, 0::2] -= np.tile(anchors[:, 2] * 0.5, (2, 1)).T 85 | anchors[:, 1::2] -= np.tile(anchors[:, 3] * 0.5, (2, 1)).T 86 | return anchors 87 | 88 | # forward from https://github.com/facebookresearch/maskrcnn-benchmark/blob/master/maskrcnn_benchmark/modeling/rpn/anchor_generator.py 89 | def forward(self, grid_sizes): 90 | 91 | anchors = [] 92 | for size, stride, base_anchors in zip(grid_sizes, self.strides, self.cell_anchors): 93 | grid_height, grid_width = size 94 | device = base_anchors.device 95 | shifts_x = torch.arange(0, grid_width, dtype=torch.float32).cuda() 96 | shifts_y = torch.arange(0, grid_height, dtype=torch.float32).cuda() 97 | shift_y, shift_x = torch.meshgrid(shifts_y, shifts_x) 98 | shift_x = (shift_x.reshape(-1) + 0.5) * stride 99 | shift_y = (shift_y.reshape(-1) + 0.5) * stride 100 | shifts = torch.stack((shift_x, shift_y, shift_x, shift_y), dim=1) 101 | anchors.append( (shifts.view(-1, 1, 4) + base_anchors.view(1, -1, 4)).reshape(-1, 4) ) 102 | 103 | return torch.cat(anchors, 0) 104 | 105 | -------------------------------------------------------------------------------- /modules/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import socket 4 | import getpass 5 | def str2bool(v): 6 | return v.lower() in ("yes", "true", "t", "1") 7 | 8 | def get_class_names(dataset): 9 | classes = { 10 | 'coco':['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush'], 11 | 'voc':['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor'], 12 | } 13 | return classes[dataset] 14 | 15 | def copy_source(source_dir): 16 | if not os.path.isdir(source_dir): 17 | os.system('mkdir -p ' + source_dir) 18 | 19 | for dirpath, dirs, files in os.walk('./', topdown=True): 20 | for file in files: 21 | if file.endswith('.py'): #fnmatch.filter(files, filepattern): 22 | shutil.copy2(os.path.join(dirpath, file), source_dir) 23 | 24 | def set_args(args, iftest='train'): 25 | 26 | if iftest == 'test': 27 | args.eval_iters = [int(val) for val in args.eval_iters.split(',')] 28 | else: 29 | args.milestones = [int(val) for val in args.milestones.split(',')] 30 | args.gammas = [float(val) for val in args.gammas.split(',')] 31 | 32 | args.dataset = args.dataset.lower() 33 | args.basenet = args.basenet.lower() 34 | 35 | args.means =[0.485, 0.456, 0.406] 36 | args.stds = [0.229, 0.224, 0.225] 37 | 38 | username = getpass.getuser() 39 | hostname = socket.gethostname() 40 | args.hostname = hostname 41 | args.user = username 42 | 43 | print('Your working directories are', args.data_root, args.save_root) 44 | return args 45 | 46 | def create_exp_name(args): 47 | return 'FPN{:d}x{:d}-{:01d}-{:s}-{:s}-hl{:01d}s{:01d}-bn{:d}f{:d}b{:d}-bs{:02d}-{:s}-lr{:06d}-{:s}'.format( 48 | args.min_size, args.max_size, int(args.multi_scale), args.dataset, args.basenet, 49 | args.num_head_layers, args.shared_heads, int(args.fbn), args.freezeupto, int(args.use_bias), 50 | args.batch_size, args.optim, int(args.lr * 1000000), args.loss_type) 51 | 52 | # Freeze batch normlisation layers 53 | def set_bn_eval(m): 54 | classname = m.__class__.__name__ 55 | if classname.find('BatchNorm') > -1: 56 | m.eval() 57 | if m.affine: 58 | m.weight.requires_grad = False 59 | m.bias.requires_grad = False 60 | 61 | 62 | def eval_strings(): 63 | return ["Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = ", 64 | "Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = ", 65 | "Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = ", 66 | "Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = ", 67 | "Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = ", 68 | "Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = ", 69 | "Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = ", 70 | "Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = ", 71 | "Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = ", 72 | "Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = ", 73 | "Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = ", 74 | "Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = "] -------------------------------------------------------------------------------- /commands.txt: -------------------------------------------------------------------------------- 1 | python train.py --loss=mbox --min_size=200 --max_size=360 --basenet=resnet18 && python train.py --loss=mbox --min_size=400 --max_size=720 --basenet=resnet18 && python train.py --loss=mbox --min_size=600 --max_size=1080 --basenet=resnet18 && python train.py --loss=mbox --min_size=800 --max_size=1440 --basenet=resnet18 && python train.py --loss=mbox --min_size=200 --max_size=360 --basenet=resnet34 && python train.py --loss=mbox --min_size=400 --max_size=720 --basenet=resnet34 && python train.py --loss=mbox --min_size=600 --max_size=1080 --basenet=resnet34 && python train.py --loss=mbox --min_size=800 --max_size=1440 --basenet=resnet34 && python train.py --loss=mbox --min_size=200 --max_size=360 --basenet=resnet50 && python train.py --loss=mbox --min_size=400 --max_size=720 --basenet=resnet50 && python train.py --loss=mbox --min_size=600 --max_size=1080 --basenet=resnet50 && python train.py --loss=mbox --min_size=800 --max_size=1440 --basenet=resnet50 && python train.py --loss=mbox --min_size=200 --max_size=360 --basenet=resnet101 && python train.py --loss=mbox --min_size=400 --max_size=720 --basenet=resnet101 && python train.py --loss=mbox --min_size=600 --max_size=1080 --basenet=resnet101 && python train.py --loss=mbox --min_size=800 --max_size=1440 --basenet=resnet101 2 | 3 | 4 | 5 | python train.py --loss=focal --min_size=200 --max_size=360 --basenet=resnet34 && python train.py --loss=focal --min_size=400 --max_size=720 --basenet=resnet34 && python train.py --loss=focal --min_size=600 --max_size=1080 --basenet=resnet34 && python train.py --loss=focal --min_size=800 --max_size=1440 --basenet=resnet34 && python train.py --loss=focal --min_size=200 --max_size=360 --basenet=resnet50 && python train.py --loss=focal --min_size=400 --max_size=720 --basenet=resnet50 && python train.py --loss=focal --min_size=600 --max_size=1080 --basenet=resnet50 && python train.py --loss=focal --min_size=800 --max_size=1440 --basenet=resnet50 && python train.py --loss=focal --min_size=200 --max_size=360 --basenet=resnet18 && python train.py --loss=focal --min_size=400 --max_size=720 --basenet=resnet18 && python train.py --loss=focal --min_size=600 --max_size=1080 --basenet=resnet18 && python train.py --loss=focal --min_size=800 --max_size=1440 --basenet=resnet18 && python train.py --loss=focal --min_size=200 --max_size=360 --basenet=resnet101 && python train.py --loss=focal --min_size=400 --max_size=720 --basenet=resnet101 && python train.py --loss=focal --min_size=600 --max_size=1080 --basenet=resnet101 && python train.py --loss=focal --min_size=800 --max_size=1440 --basenet=resnet101 6 | 7 | python evaluate.py --loss=mbox --min_size=200 --max_size=360 --basenet=resnet18 && python evaluate.py --loss=mbox --min_size=400 --max_size=720 --basenet=resnet18 && python evaluate.py --loss=mbox --min_size=600 --max_size=1080 --basenet=resnet18 && python evaluate.py --loss=mbox --min_size=800 --max_size=1440 --basenet=resnet18 && python evaluate.py --loss=mbox --min_size=200 --max_size=360 --basenet=resnet34 && python evaluate.py --loss=mbox --min_size=400 --max_size=720 --basenet=resnet34 && python evaluate.py --loss=mbox --min_size=600 --max_size=1080 --basenet=resnet34 && python evaluate.py --loss=mbox --min_size=800 --max_size=1440 --basenet=resnet34 && python evaluate.py --loss=mbox --min_size=200 --max_size=360 --basenet=resnet50 && python evaluate.py --loss=mbox --min_size=400 --max_size=720 --basenet=resnet50 && python evaluate.py --loss=mbox --min_size=600 --max_size=1080 --basenet=resnet50 && python evaluate.py --loss=mbox --min_size=800 --max_size=1440 --basenet=resnet50 && python evaluate.py --loss=mbox --min_size=200 --max_size=360 --basenet=resnet101 && python evaluate.py --loss=mbox --min_size=400 --max_size=720 --basenet=resnet101 && python evaluate.py --loss=mbox --min_size=600 --max_size=1080 --basenet=resnet101 && python evaluate.py --loss=mbox --min_size=800 --max_size=1440 --basenet=resnet101 8 | 9 | 10 | 11 | python evaluate.py --loss=focal --min_size=200 --max_size=360 --basenet=resnet34 && python evaluate.py --loss=focal --min_size=400 --max_size=720 --basenet=resnet34 && python evaluate.py --loss=focal --min_size=600 --max_size=1080 --basenet=resnet34 && python evaluate.py --loss=focal --min_size=800 --max_size=1440 --basenet=resnet34 && python evaluate.py --loss=focal --min_size=200 --max_size=360 --basenet=resnet50 && python evaluate.py --loss=focal --min_size=400 --max_size=720 --basenet=resnet50 && python evaluate.py --loss=focal --min_size=600 --max_size=1080 --basenet=resnet50 && python evaluate.py --loss=focal --min_size=800 --max_size=1440 --basenet=resnet50 && python evaluate.py --loss=focal --min_size=200 --max_size=360 --basenet=resnet18 && python evaluate.py --loss=focal --min_size=400 --max_size=720 --basenet=resnet18 && python evaluate.py --loss=focal --min_size=600 --max_size=1080 --basenet=resnet18 && python evaluate.py --loss=focal --min_size=800 --max_size=1440 --basenet=resnet18 && python evaluate.py --loss=focal --min_size=200 --max_size=360 --basenet=resnet101 && python evaluate.py --loss=focal --min_size=400 --max_size=720 --basenet=resnet101 && python evaluate.py --loss=focal --min_size=600 --max_size=1080 --basenet=resnet101 && python evaluate.py --loss=focal --min_size=800 --max_size=1440 --basenet=resnet101 -------------------------------------------------------------------------------- /models/retinanet_shared_heads.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | """ 4 | 5 | FPN network Classes 6 | 7 | Author: Gurkirt Singh 8 | Inspired from https://github.com/kuangliu/pytorch-retinanet and 9 | https://github.com/gurkirt/realtime-action-detection 10 | 11 | """ 12 | from modules.anchor_box_retinanet import anchorBox 13 | from modules.detection_loss import MultiBoxLoss, YOLOLoss, FocalLoss 14 | from models.backbone_models import backbone_models 15 | from modules.box_utils import decode 16 | import torch, math, pdb, math 17 | import torch.nn as nn 18 | 19 | class RetinaNet(nn.Module): 20 | """Feature Pyramid Network Architecture 21 | The network is composed of a backbone FPN network followed by the 22 | added Head conv layers. 23 | Each head layer branches into 24 | 1) conv2d for class conf scores 25 | 2) conv2d for localization predictions 26 | 27 | See: 28 | RetinaNet: https://arxiv.org/pdf/1708.02002.pdf for more details. 29 | FPN: https://arxiv.org/pdf/1612.03144.pdf 30 | 31 | Args: 32 | backbone Network: 33 | Program Argument Namespace 34 | 35 | """ 36 | 37 | def __init__(self, backbone, args): 38 | super(RetinaNet, self).__init__() 39 | 40 | self.num_classes = args.num_classes 41 | # TODO: implement __call__ in 42 | 43 | self.anchors = anchorBox() 44 | self.ar = self.anchors.ar 45 | args.ar = self.ar 46 | self.use_bias = args.use_bias 47 | self.head_size = args.head_size 48 | self.backbone_net = backbone 49 | self.shared_heads = args.shared_heads 50 | self.num_head_layers = args.num_head_layers 51 | 52 | assert self.shared_heads0: 55 | self.features_layers = self.make_features(self.shared_heads) 56 | self.reg_heads = self.make_head(self.ar * 4, self.num_head_layers - self.shared_heads) 57 | self.cls_heads = self.make_head(self.ar * self.num_classes, self.num_head_layers - self.shared_heads) 58 | 59 | if args.loss_type != 'mbox': 60 | self.prior_prob = 0.01 61 | bias_value = -math.log((1 - self.prior_prob ) / self.prior_prob ) 62 | nn.init.constant_(self.cls_heads[-1].bias, bias_value) 63 | if not hasattr(args, 'eval_iters'): # eval_iters only in test case 64 | if args.loss_type == 'mbox': 65 | self.criterion = MultiBoxLoss(args.positive_threshold) 66 | elif args.loss_type == 'yolo': 67 | self.criterion = YOLOLoss(args.positive_threshold, args.negative_threshold) 68 | elif args.loss_type == 'focal': 69 | self.criterion = FocalLoss(args.positive_threshold, args.negative_threshold) 70 | else: 71 | error('Define correct loss type') 72 | 73 | 74 | def forward(self, images, gts=None, counts=None,get_features=False): 75 | sources = self.backbone_net(images) 76 | features = list() 77 | # pdb.set_trace() 78 | if self.shared_heads>0: 79 | for x in sources: 80 | features.append(self.features_layers(x)) 81 | else: 82 | features = sources 83 | 84 | grid_sizes = [feature_map.shape[-2:] for feature_map in features] 85 | ancohor_boxes = self.anchors(grid_sizes) 86 | 87 | loc = list() 88 | conf = list() 89 | 90 | for x in features: 91 | loc.append(self.reg_heads(x).permute(0, 2, 3, 1).contiguous()) 92 | conf.append(self.cls_heads(x).permute(0, 2, 3, 1).contiguous()) 93 | 94 | loc = torch.cat([o.view(o.size(0), -1) for o in loc], 1) 95 | conf = torch.cat([o.view(o.size(0), -1) for o in conf], 1) 96 | 97 | flat_loc = loc.view(loc.size(0), -1, 4) 98 | flat_conf = conf.view(conf.size(0), -1, self.num_classes) 99 | # pdb.set_trace() 100 | if get_features: # testing mode with feature return 101 | return torch.stack([decode(flat_loc[b], ancohor_boxes) for b in range(flat_loc.shape[0])], 0), flat_conf, features 102 | elif gts is not None: # training mode 103 | return self.criterion(flat_conf, flat_loc, gts, counts, ancohor_boxes) 104 | else: # otherwise testing mode 105 | return torch.stack([decode(flat_loc[b], ancohor_boxes) for b in range(flat_loc.shape[0])], 0), flat_conf 106 | 107 | 108 | def make_features(self, shared_heads): 109 | layers = [] 110 | use_bias = self.use_bias 111 | head_size = self.head_size 112 | for _ in range(shared_heads): 113 | layers.append(nn.Conv2d(head_size, head_size, kernel_size=3, stride=1, padding=1, bias=use_bias)) 114 | layers.append(nn.ReLU(True)) 115 | 116 | layers = nn.Sequential(*layers) 117 | 118 | for m in layers.modules(): 119 | if isinstance(m, nn.Conv2d): 120 | nn.init.normal_(m.weight, mean=0, std=0.01) 121 | if hasattr(m.bias, 'data'): 122 | nn.init.constant_(m.bias, 0) 123 | 124 | return layers 125 | 126 | def make_head(self, out_planes, nun_shared_heads): 127 | layers = [] 128 | use_bias = self.use_bias 129 | head_size = self.head_size 130 | for _ in range(nun_shared_heads): 131 | layers.append(nn.Conv2d(head_size, head_size, kernel_size=3, stride=1, padding=1, bias=use_bias)) 132 | layers.append(nn.ReLU(True)) 133 | 134 | layers.append(nn.Conv2d(head_size, out_planes, kernel_size=3, stride=1, padding=1)) 135 | layers = nn.Sequential(*layers) 136 | 137 | for m in layers.modules(): 138 | if isinstance(m, nn.Conv2d): 139 | nn.init.normal_(m.weight, mean=0, std=0.01) 140 | if hasattr(m.bias, 'data'): 141 | nn.init.constant_(m.bias, 0) 142 | 143 | return layers 144 | 145 | def build_retinanet_shared_heads(args): 146 | # print('basenet', args.basenet) 147 | backbone = backbone_models(args.basenet, args.model_dir, args.use_bias) 148 | # print('backbone model::', backbone) 149 | return RetinaNet(backbone, args) 150 | -------------------------------------------------------------------------------- /data/detectionDatasets.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (c) 2019 Gurkirt Singh 4 | All Rights Reserved. 5 | 6 | """ 7 | 8 | import json 9 | import csv 10 | import torch 11 | import pdb, time 12 | import torch.utils.data as data 13 | import pickle 14 | from .transforms import get_image_list_resized 15 | import torch.nn.functional as F 16 | import numpy as np 17 | from PIL import ImageFile 18 | ImageFile.LOAD_TRUNCATED_IMAGES = True 19 | from PIL import Image, ImageDraw 20 | import glob 21 | import pdb 22 | 23 | def read_file(path, full_test): 24 | try: 25 | with open(path, 'r') as f: 26 | lines = f.readlines() 27 | except: 28 | lines = None 29 | 30 | if lines is None and full_test: 31 | # in case we are testing and we don't have labels 32 | # but we need to return at least one label per image 33 | # we fake it like belwo 34 | return [[0.25, 0.25, 0.75, 0.75, -1]] 35 | 36 | if not lines: 37 | # during training or validation we return None as labels so we can skip the image 38 | return None 39 | 40 | 41 | # during training or validation we return the labels 42 | lines = [line.split(' ') for line in lines if len(line)>0] 43 | 44 | out_data = [] 45 | for line in lines: 46 | line_entries = [float(entry) for entry in line] 47 | line_entries = [line_entries[1], line_entries[2], line_entries[3], line_entries[4], line_entries[0]] 48 | out_data.append(line_entries) 49 | 50 | return out_data 51 | 52 | def read_labels(image_files, full_test): 53 | labels=[] 54 | 55 | for img_path in image_files: 56 | label_file = img_path.replace('.jpg', '.txt') 57 | label= read_file(label_file, full_test) 58 | if label is not None: 59 | labels.append([img_path, label]) 60 | 61 | return labels 62 | 63 | 64 | def read_sets(path, input_sets=['train/set1','train/set2'], full_test=False): 65 | 66 | all_files=[] 67 | for set_name in input_sets: 68 | set_path= path + set_name 69 | image_files= glob.glob(set_path+'/*.jpg') 70 | all_files.extend(image_files) 71 | 72 | labels= read_labels(all_files, full_test) 73 | print('length of labels', len(labels)) 74 | return(labels) 75 | 76 | 77 | def make_object_lists(rootpath, input_sets=['train/set1','train/set2'], full_test=False): 78 | ''' 79 | 80 | input_sets has be a list of set needs tobe read : 81 | e.g. 'train/set1','train/set2', 'val/obj', or 'test/obj' 82 | 83 | 84 | ''' 85 | 86 | with open(rootpath+'train/obj.names', 'r') as fil: 87 | cls_list= fil.read().split('\n') 88 | 89 | cls_list = [name for name in cls_list if len(name)>0] 90 | 91 | final_labels= read_sets(rootpath, input_sets, full_test) 92 | 93 | return(cls_list, final_labels) 94 | 95 | 96 | def resize(image, size): 97 | image = F.interpolate(image.unsqueeze(0), size=size, mode="nearest").squeeze(0) 98 | return image 99 | 100 | 101 | class DetectionDataset(data.Dataset): 102 | """Detection Dataset class for pytorch dataloader""" 103 | 104 | def __init__(self, root, train=False, input_sets=['train/set1','train/set2'], transform=None, anno_transform=None, full_test=False): 105 | 106 | self.train = train 107 | self.root= root 108 | self.input_sets = input_sets 109 | self.transform = transform 110 | self.anno_transform = anno_transform 111 | self.ids = list() 112 | self.classes, self.ids = make_object_lists(self.root, input_sets=input_sets, full_test=full_test) 113 | self.print_str= '' 114 | self.max_targets = 20 115 | 116 | 117 | def __len__(self): 118 | return len(self.ids) 119 | 120 | def __getitem__(self, index): 121 | annot_info = self.ids[index] 122 | 123 | img_path = annot_info[0] 124 | bbox_info = np.array(annot_info[1]) 125 | labels= bbox_info[:,4] 126 | 127 | x1= bbox_info[:,0] - bbox_info[:,2]/2 128 | y1= bbox_info[:,1] - bbox_info[:,3]/2 129 | x2= bbox_info[:,0] + bbox_info[:,2]/2 130 | y2= bbox_info[:,1] + bbox_info[:,3]/2 131 | 132 | boxes= np.hstack([np.expand_dims(x1, axis=1), np.expand_dims(y1, axis=1), 133 | np.expand_dims(x2, axis=1), np.expand_dims(y2, axis=1)]) 134 | # Box coordinates should be in ranges of [0,1] 135 | 136 | img = Image.open(img_path).convert('RGB') 137 | orig_w, orig_h = img.size 138 | 139 | ## Horizontal flip is turned off because some catories are sentive to the flip. 140 | # if self.train and np.random.random() < 0.5: 141 | # img = img.transpose(Image.FLIP_LEFT_RIGHT) 142 | # w = boxes[:, 2] - boxes[:, 0] 143 | # boxes[:, 0] = 1 - boxes[:, 2] # boxes should be in x1 y1 x2 y2 [0,1] format 144 | # boxes[:, 2] = boxes[:, 0] + w # boxes should be in x1 y1 x2 y2 [0,1] format 145 | 146 | if self.transform is not None: 147 | img = self.transform(img) 148 | 149 | _, height, width = img.shape 150 | 151 | wh = [width, height, orig_w, orig_h] 152 | # print(wh) 153 | boxes[:, 0] *= width # width x1 154 | boxes[:, 2] *= width # width x2 155 | boxes[:, 1] *= height # height y1 156 | boxes[:, 3] *= height # height y2 157 | 158 | targets = np.hstack((boxes, np.expand_dims(labels, axis=1))) 159 | return img, targets, index, wh 160 | 161 | 162 | def custum_collate(batch): 163 | targets = [] 164 | images = [] 165 | image_ids = [] 166 | whs = [] 167 | # fno = [] 168 | # rgb_images, flow_images, aug_bxsl, prior_labels, prior_gt_locations, num_mt, index 169 | 170 | for sample in batch: 171 | images.append(sample[0]) 172 | targets.append(torch.FloatTensor(sample[1])) 173 | image_ids.append(sample[2]) 174 | whs.append(sample[3]) 175 | 176 | counts = [] 177 | max_len = -1 178 | for target in targets: 179 | max_len = max(max_len, target.shape[0]) 180 | counts.append(target.shape[0]) 181 | new_targets = torch.zeros(len(targets), max_len, targets[0].shape[1]) 182 | cc = 0 183 | for target in targets: 184 | new_targets[cc,:target.shape[0]] = target 185 | cc += 1 186 | images_ = get_image_list_resized(images) 187 | cts = torch.LongTensor(counts) 188 | # print(images_.shape) 189 | return images_, new_targets, cts, image_ids, whs 190 | 191 | # if __name__== '__main__': 192 | # # from torchvision import transforms 193 | # dataset= DetectionDataset(root= folder, image_sets= 'train') 194 | # dataset_val= DetectionDataset(root= folder, image_sets= 'val') 195 | # print('train',len(dataset)) 196 | # print('val',len(dataset_val)) 197 | -------------------------------------------------------------------------------- /modules/evaluation.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 3 | Authot Gurkirt Singh 4 | ''' 5 | 6 | import os 7 | import time 8 | import numpy as np 9 | import scipy.io as io # to save detection as mat files 10 | 11 | def voc_ap(rec, prec, use_07_metric=False): 12 | """ ap = voc_ap(rec, prec, [use_07_metric]) 13 | Compute VOC AP given precision and recall. 14 | If use_07_metric is true, uses the 15 | VOC 07 11 point method (default:False). 16 | """ 17 | # print('voc_ap() - use_07_metric:=' + str(use_07_metric)) 18 | if use_07_metric: 19 | # 11 point metric 20 | ap = 0. 21 | for t in np.arange(0., 1.1, 0.1): 22 | if np.sum(rec >= t) == 0: 23 | p = 0 24 | else: 25 | p = np.max(prec[rec >= t]) 26 | ap = ap + p / 11. 27 | else: 28 | # correct AP calculation 29 | # first append sentinel values at the end 30 | mrec = np.concatenate(([0.], rec, [1.])) 31 | mpre = np.concatenate(([0.], prec, [0.])) 32 | 33 | # compute the precision envelope 34 | for i in range(mpre.size - 1, 0, -1): 35 | mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i]) 36 | 37 | # to calculate area under PR curve, look for points 38 | # where X axis (recall) changes value 39 | i = np.where(mrec[1:] != mrec[:-1])[0] 40 | 41 | # and sum (\Delta recall) * prec 42 | ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1]) 43 | return ap 44 | 45 | 46 | def get_gt_of_cls(gt_boxes, cls): 47 | cls_gt_boxes = [] 48 | for i in range(len(gt_boxes)): 49 | if gt_boxes[i,-1] == cls: 50 | cls_gt_boxes.append(gt_boxes[i, :-1]) 51 | return np.asarray(cls_gt_boxes) 52 | 53 | 54 | def compute_iou(cls_gt_boxes, box): 55 | ious = np.zeros(cls_gt_boxes.shape[0]) 56 | 57 | for m in range(ious.shape[0]): 58 | gtbox = cls_gt_boxes[m] 59 | 60 | xmin = max(gtbox[0], box[0]) 61 | ymin = max(gtbox[1], box[1]) 62 | xmax = min(gtbox[2], box[2]) 63 | ymax = min(gtbox[3], box[3]) 64 | iw = np.maximum(xmax - xmin, 0.) 65 | ih = np.maximum(ymax - ymin, 0.) 66 | if iw>0 and ih>0: 67 | intsc = iw*ih 68 | else: 69 | intsc = 0.0 70 | # print (intsc) 71 | union = (gtbox[2] - gtbox[0]) * (gtbox[3] - gtbox[1]) + (box[2] - box[0]) * (box[3] - box[1]) - intsc 72 | ious[m] = float(intsc)/float(union) 73 | 74 | return ious 75 | 76 | def evaluate_detections(gt_boxes, det_boxes, CLASSES=[], iou_thresh=0.5): 77 | 78 | ap_strs = [] 79 | num_frames = len(gt_boxes) 80 | print('Evaluating for ', num_frames, 'frames') 81 | ap_all = np.zeros(len(CLASSES), dtype=np.float32) 82 | for cls_ind, cls in enumerate(CLASSES): # loop over each class 'cls' 83 | scores = np.zeros(num_frames * 2000) 84 | istp = np.zeros(num_frames * 2000) 85 | det_count = 0 86 | num_postives = 0.0 87 | for nf in range(num_frames): # loop over each frame 'nf' 88 | # if len(gt_boxes[nf])>0 and len(det_boxes[cls_ind][nf]): 89 | frame_det_boxes = np.copy(det_boxes[cls_ind][nf]) # get frame detections for class cls in nf 90 | cls_gt_boxes = get_gt_of_cls(np.copy(gt_boxes[nf]), cls_ind) # get gt boxes for class cls in nf frame 91 | num_postives += cls_gt_boxes.shape[0] 92 | if frame_det_boxes.shape[0]>0: # check if there are dection for class cls in nf frame 93 | argsort_scores = np.argsort(-frame_det_boxes[:,-1]) # sort in descending order 94 | for i, k in enumerate(argsort_scores): # start from best scoring detection of cls to end 95 | box = frame_det_boxes[k, :-1] # detection bounfing box 96 | score = frame_det_boxes[k,-1] # detection score 97 | ispositive = False # set ispostive to false every time 98 | if cls_gt_boxes.shape[0]>0: # we can only find a postive detection 99 | # if there is atleast one gt bounding for class cls is there in frame nf 100 | iou = compute_iou(cls_gt_boxes, box) # compute IOU between remaining gt boxes 101 | # and detection boxes 102 | maxid = np.argmax(iou) # get the max IOU window gt index 103 | if iou[maxid] >= iou_thresh: # check is max IOU is greater than detection threshold 104 | ispositive = True # if yes then this is ture positive detection 105 | cls_gt_boxes = np.delete(cls_gt_boxes, maxid, 0) # remove assigned gt box 106 | scores[det_count] = score # fill score array with score of current detection 107 | if ispositive: 108 | istp[det_count] = 1 # set current detection index (det_count) 109 | # to 1 if it is true postive example 110 | det_count += 1 111 | 112 | if num_postives<1: 113 | num_postives =1 114 | scores = scores[:det_count] 115 | istp = istp[:det_count] 116 | argsort_scores = np.argsort(-scores) # sort in descending order 117 | istp = istp[argsort_scores] # reorder istp's on score sorting 118 | fp = np.cumsum(istp == 0) # get false positives 119 | tp = np.cumsum(istp == 1) # get true positives 120 | fp = fp.astype(np.float64) 121 | tp = tp.astype(np.float64) 122 | recall = tp / float(num_postives) # compute recall 123 | precision = tp / np.maximum(tp + fp, np.finfo(np.float64).eps) # compute precision 124 | cls_ap = voc_ap(recall, precision) # compute average precision using voc2007 metric 125 | ap_all[cls_ind] = cls_ap 126 | # print(cls_ind,CLASSES[cls_ind], cls_ap) 127 | ap_str = str(CLASSES[cls_ind]) + ' : ' + str(num_postives) + ' : ' + str(det_count) + ' : ' + str(cls_ap) 128 | ap_strs.append(ap_str) 129 | 130 | # print ('mean ap ', np.mean(ap_all)) 131 | return np.mean(ap_all), ap_all, ap_strs, det_boxes 132 | 133 | 134 | def save_detection_framewise(det_boxes, image_ids, iteration): 135 | det_save_dir = '/mnt/mars-beta/gur-workspace/use-ssd-data/UCF101/detections/RGB-01-{:06d}/'.format(iteration) 136 | print('Saving detections to', det_save_dir) 137 | num_images = len(image_ids) 138 | for idx in range(num_images): 139 | img_id = image_ids[idx] 140 | save_path = det_save_dir+img_id[:-5] 141 | if not os.path.isdir(save_path): 142 | os.system('mkdir -p '+save_path) 143 | fid = open(det_save_dir+img_id+'.txt','w') 144 | for cls_ind in range(len(det_boxes)): 145 | frame_det_boxes = det_boxes[cls_ind][idx] 146 | for d in range(len(frame_det_boxes)): 147 | line = str(cls_ind+1) 148 | for k in range(5): 149 | line += ' {:f}'.format(frame_det_boxes[d,k]) 150 | line += '\n' 151 | fid.write(line) 152 | fid.close() 153 | 154 | -------------------------------------------------------------------------------- /models/resnetFPN.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | import math, pdb 3 | import torch.nn.functional as F 4 | import torch 5 | from torch.nn.parameter import Parameter 6 | 7 | ### Download weights from https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py 8 | def conv3x3(in_planes, out_planes, stride=1, padding=1, bias=False): 9 | """3x3 convolution with padding""" 10 | return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, 11 | padding=padding, bias=bias) 12 | 13 | def conv1x1(in_channel, out_channel, **kwargs): 14 | return nn.Conv2d(in_channel, out_channel, kernel_size=1, **kwargs) 15 | 16 | 17 | class BasicBlock(nn.Module): 18 | 19 | expansion = 1 20 | 21 | def __init__(self, inplanes, planes, stride=1, downsample=None): 22 | super(BasicBlock, self).__init__() 23 | self.conv1 = conv3x3(inplanes, planes, stride) 24 | self.bn1 = nn.BatchNorm2d(planes) 25 | self.relu = nn.ReLU(inplace=True) 26 | self.conv2 = conv3x3(planes, planes) 27 | self.bn2 = nn.BatchNorm2d(planes) 28 | self.downsample = downsample 29 | self.stride = stride 30 | 31 | def forward(self, x): 32 | residual = x 33 | 34 | out = self.conv1(x) 35 | out = self.bn1(out) 36 | out = self.relu(out) 37 | 38 | out = self.conv2(out) 39 | out = self.bn2(out) 40 | 41 | if self.downsample is not None: 42 | residual = self.downsample(x) 43 | 44 | out += residual 45 | out = self.relu(out) 46 | 47 | return out 48 | 49 | 50 | class Bottleneck(nn.Module): 51 | expansion = 4 52 | 53 | def __init__(self, inplanes, planes, stride=1, downsample=None): 54 | super(Bottleneck, self).__init__() 55 | self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) 56 | self.bn1 = nn.BatchNorm2d(planes) 57 | self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, 58 | padding=1, bias=False) 59 | self.bn2 = nn.BatchNorm2d(planes) 60 | self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False) 61 | self.bn3 = nn.BatchNorm2d(planes * 4) 62 | self.relu = nn.ReLU(inplace=True) 63 | self.downsample = downsample 64 | self.stride = stride 65 | 66 | def forward(self, x): 67 | residual = x 68 | 69 | out = self.conv1(x) 70 | out = self.bn1(out) 71 | out = self.relu(out) 72 | 73 | out = self.conv2(out) 74 | out = self.bn2(out) 75 | out = self.relu(out) 76 | 77 | out = self.conv3(out) 78 | out = self.bn3(out) 79 | 80 | if self.downsample is not None: 81 | residual = self.downsample(x) 82 | 83 | out += residual 84 | out = self.relu(out) 85 | 86 | return out 87 | 88 | 89 | class ResNetFPN(nn.Module): 90 | 91 | def __init__(self, block, layers, use_bias, seq_len): 92 | self.inplanes = 64 93 | super(ResNetFPN, self).__init__() 94 | self.conv1 = nn.Conv2d(3*seq_len, 64, kernel_size=7, stride=2, padding=3, 95 | bias=False) 96 | self.bn1 = nn.BatchNorm2d(64) 97 | self.relu = nn.ReLU(inplace=True) 98 | 99 | self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) 100 | self.layer1 = self._make_layer(block, 64, layers[0]) 101 | self.layer2 = self._make_layer(block, 128, layers[1], stride=2) 102 | self.layer3 = self._make_layer(block, 256, layers[2], stride=2) 103 | self.layer4 = self._make_layer(block, 512, layers[3], stride=2) 104 | 105 | #self.avgpool = nn.AvgPool2d(7, stride=1) 106 | #self.fc = nn.Linear(512 * block.expansion, num_classes) 107 | 108 | self.conv6 = conv3x3(512 * block.expansion, 256, stride=2, padding=1, bias=use_bias) # P6 109 | self.conv7 = conv3x3(256, 256, stride=2, padding=1, bias=use_bias) # P7 110 | 111 | self.lateral_layer1 = conv1x1(512 * block.expansion, 256, bias=use_bias) 112 | self.lateral_layer2 = conv1x1(256 * block.expansion, 256, bias=use_bias) 113 | self.lateral_layer3 = conv1x1(128 * block.expansion, 256, bias=use_bias) 114 | 115 | self.corr_layer1 = conv3x3(256, 256, stride=1, padding=1, bias=use_bias) # P4 116 | self.corr_layer2 = conv3x3(256, 256, stride=1, padding=1, bias=use_bias) # P4 117 | self.corr_layer3 = conv3x3(256, 256, stride=1, padding=1, bias=use_bias) # P3 118 | 119 | for m in self.modules(): 120 | if isinstance(m, nn.Conv2d): 121 | torch.nn.init.kaiming_uniform_(m.weight, a=1) 122 | if hasattr(m.bias, 'data'): 123 | torch.nn.init.constant_(m.bias, 0) 124 | elif isinstance(m, nn.BatchNorm2d): 125 | m.weight.data.fill_(1) 126 | m.bias.data.zero_() 127 | 128 | 129 | def _upsample(self, x, y): 130 | _, _, h, w = y.size() 131 | x_upsampled = F.interpolate(x, [h, w], mode='nearest') 132 | 133 | return x_upsampled 134 | 135 | def _make_layer(self, block, planes, blocks, stride=1): 136 | 137 | downsample = None 138 | if stride != 1 or self.inplanes != planes * block.expansion: 139 | downsample = nn.Sequential( 140 | nn.Conv2d(self.inplanes, planes * block.expansion, 141 | kernel_size=1, stride=stride, bias=False), 142 | nn.BatchNorm2d(planes * block.expansion), 143 | ) 144 | 145 | layers = [] 146 | layers.append(block(self.inplanes, planes, stride, downsample)) 147 | self.inplanes = planes * block.expansion 148 | for i in range(1, blocks): 149 | layers.append(block(self.inplanes, planes)) 150 | 151 | return nn.Sequential(*layers) 152 | 153 | def forward(self, x): 154 | # pdb.set_trace() 155 | x = self.conv1(x) 156 | x = self.bn1(x) 157 | x = self.relu(x) 158 | x = self.maxpool(x) 159 | # print('x0 DATA ', torch.sum(x.data)) 160 | x = self.layer1(x) 161 | # print('x DATA ', torch.sum(x.data)) 162 | c3 = self.layer2(x) 163 | # print('c3 DATA ', torch.sum(c3.data)) 164 | c4 = self.layer3(c3) 165 | c5 = self.layer4(c4) 166 | 167 | # Top-down 168 | p5 = self.lateral_layer1(c5) 169 | p5_upsampled = self._upsample(p5, c4) 170 | p5 = self.corr_layer1(p5) 171 | 172 | p4 = self.lateral_layer2(c4) 173 | p4 = p5_upsampled + p4 174 | p4_upsampled = self._upsample(p4, c3) 175 | p4 = self.corr_layer2(p4) 176 | 177 | p3 = self.lateral_layer3(c3) 178 | p3 = p4_upsampled + p3 179 | p3 = self.corr_layer3(p3) 180 | 181 | p6 = self.conv6(c5) 182 | p7 = self.conv7(F.relu(p6)) 183 | 184 | return p3, p4, p5, p6, p7 185 | 186 | def load_my_state_dict(self, state_dict, seq_len=1): 187 | 188 | ### Download weights from https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py 189 | own_state = self.state_dict() 190 | # print(own_state.keys()) 191 | for name, param in state_dict.items(): 192 | # pdb.set_trace() 193 | if name in own_state.keys(): 194 | # print(name) 195 | if isinstance(param, Parameter): 196 | # backwards compatibility for serialized parameters 197 | param = param.data 198 | if name == 'conv1.weight': 199 | print(name, 'is being filled with {:d} seq_len\n'.format(seq_len)) 200 | param = param.repeat(1, seq_len, 1, 1) 201 | param = param / float(seq_len) 202 | try: 203 | own_state[name].copy_(param) 204 | except Exception: 205 | raise RuntimeError('While copying the parameter named {}, ' 206 | 'whose dimensions in the model are {} and ' 207 | 'whose dimensions in the checkpoint are {}.' 208 | .format(name, own_state[name].size(), param.size())) 209 | else: 210 | print('NAME IS NOT IN OWN STATE::>' + name) 211 | 212 | def resnetfpn(perms, name, use_bias, seq_len=1): 213 | num = int(name[6:]) 214 | print('Nume', num) 215 | if num<50: 216 | return ResNetFPN(BasicBlock, perms, use_bias, seq_len) 217 | else: 218 | return ResNetFPN(Bottleneck, perms, use_bias, seq_len) 219 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Surgeon Action Detection for endoscopic images/videos 2 | ### This is a baseline model developed for SARAS-ESAD 2020 challenge. To download the dataset and participate in the challenge, please register at [SARAS-ESAD website](https://saras-esad.grand-challenge.org). 3 | 4 | The code is adopated from [RetinaNet implementation in pytorch.1.x](https://github.com/gurkirt/RetinaNet.pytorch.1.x). 5 | 6 | ## Features of this baseline 7 | 8 | - Data preparation instructions for SARAS-ESAD 2020 challenge 9 | - Dataloader for SARAS-ESAD dataset 10 | - Pytorch1.X implementation 11 | - Feature pyramid network (FPN) architecture with different ResNet backbones 12 | - Three types of loss functions i.e OHEM Loss, Focal Loss, and YOLO Loss on top of FPN 13 | 14 | ## Introduction 15 | 16 | Here, we implement basic data-handling tools for [SARAS-ESAD](https://saras-esad.grand-challenge.org/Dataset/) dataset with FPN training process. We implement a pure pytorch code for train FPN with [Focal-Loss](https://arxiv.org/pdf/1708.02002.pdf) or [OHEM/multi-box-loss](https://arxiv.org/pdf/1512.02325.pdf) paper. 17 | 18 | 19 | We hope this will help kick start more teams to get up to the speed and allow the time for more innovative solutions. We want to eliminate the pain of building data handling and training process from scratch. Our final aim is to get this repository the level of [realtime-action-detection](https://github.com/gurkirt/realtime-action-detection). 20 | 21 | At the moment we support the latest pytorch and ubuntu with Anaconda distribution of python. Tested on a single machine with 2/4/8 GPUs. 22 | 23 | You can found out about architecture and loss function on parent repository, i.e. [RetinaNet implementation in pytorch.1.x](https://github.com/gurkirt/RetinaNet.pytorch.1.x). 24 | 25 | ResNet is used as a backbone network (a) to build the pyramid features (b). 26 | Each classification (c) and regression (d) subnet is made of 4 convolutional layers and finally a convolutional layer to predict the class scores and bounding box coordinated respectively. 27 | 28 | Similar to the original paper, we freeze the batch normalisation layers of ResNet based backbone networks. Also, few initial layers are also frozen, see `fbn` flag in training arguments. 29 | 30 | ## Loss functions 31 | - OHEM with multi-box loss function: We use multi-box loss function with online hard example mining (OHEM), similar to [SSD](https://arxiv.org/pdf/1512.02325.pdf). A huge thanks to Max DeGroot, Ellis Brown for [Pytorch implementation](https://github.com/amdegroot/ssd.pytorch) of SSD and loss function. 32 | 33 | - Focal loss: Same as in the original paper we use sigmoid focal loss, see [RetinaNet](https://arxiv.org/pdf/1708.02002.pdf). We use pure pytorch implementation of it. 34 | 35 | - Yolo Loss: Multi-part loss function from [YOLO](https://pjreddie.com/darknet/yolo/) is also implemented here. 36 | 37 | ## Installation 38 | You will need the following to run this code successfully 39 | - Anaconda python 40 | - Pytorch latest 41 | - Visualisation 42 | - if you want to visualise set tensorboard flag equal to true while training 43 | - [TensorboardX](https://github.com/lanpa/tensorboardX) 44 | - Tensorflow for tensorboard 45 | 46 | 47 | ### Datasets and other downloads 48 | - Please visit [SARAS-ESAD](https://saras-esad.grand-challenge.org) website to download the dataset for surgeon action detection. 49 | - Extract all the sets (train and val) from zip files and put them under a single directory. Provide the path of that directory as data_root in train file. Data preprocessing and feeding pipeline is in [detectionDatasets.py](https://github.com/Viveksbawa/SARAS-ESAD-baseline/blob/master/data/detectionDatasets.py) file. 50 | - rename the data directory `esad`. 51 | - Your directory will look like 52 | - esad 53 | - train 54 | - set1 55 | - file.txt 56 | - file.jpg 57 | - .. 58 | - val 59 | - obj 60 | - file.txt 61 | - file.jpg 62 | - .. 63 | 64 | - Now your dataset is ready, that is time to download imagenet pretrained weights for ResNet backbone models. 65 | - Weights are initialised with imagenet pretrained models, specify the path of pre-saved models, `model_dir` in `train.py`. Download them from [torchvision models](https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py). After you have download weights, please rename then appropriately under `model_dir` e.g. resnet50 resen101 etc. from This is a requirement of the training process. 66 | 67 | ## TRAINING 68 | 69 | Once you have pre-processed the dataset, then you are ready to train your networks. 70 | We must have the following arguments set correctly: 71 | - `data_root` is base path upto `esad` directory e.g. `\home\gurkirt\` 72 | - `save_root` is a base path where you want to store the checkpoints, training logs, tensorboard logs etc. 73 | - `model_dir` is a path where ResNet backbone model weights are stored 74 | 75 | To train run the following command. 76 | 77 | ``` 78 | python train.py --loss_type=mbox --data_root=\home\gurkirt\ --tensoboard=true 79 | ``` 80 | 81 | It will use all the visible GPUs. 82 | You can append `CUDA_VISIBLE_DEVICES=` at the beginning of the above command to mask certain GPUs. We used 2 GPU machine to run our experiments. 83 | 84 | Please check the arguments in `train.py` to adjust the training process to your liking. 85 | 86 | ### Some useful flags 87 | - 88 | 89 | ## Evaluation 90 | Model is evaluated and saved after each `1000` iterations. 91 | 92 | mAP@0.25 is computed after every `500` iterations and at the end. You can change to your liking by specify it in `train.py` arguments. 93 | 94 | You can evaluate and save the results in `text` file using `evaluate.py`. It follow the same arguments `train.py`. 95 | By default it evaluate using the model store at `max_iters`, but you can change it any other snapshot/checkpoint. 96 | 97 | ``` 98 | python evaluate.py --loss_type=focal 99 | ``` 100 | 101 | This will dump a log file with **results(mAP)** on the validation set and as well as a **submission file**. 102 | 103 | ## Results 104 | Here are the results on `esad` dataset. 105 | 106 | Results of the baseline models with different loss function and input image sizes, where backbone network fixed to ResNet50. **AP_10**, **AP_30**, **AP_50**, and **AP_mean** are presented on validation-set, while **Test-AP_mean** is computed based on test-set similar to **AP_mean**. 107 | 108 | | Loss | min dim | AP_10 | AP_30 | AP_50 | AP_MEAN | Test-AP_MEAN | 109 | |----- |----- |:-------: |:--------:| :-----: | :-------:| :------: | 110 | | Focal | 200 | 33.8 | 17.7 | 6.6 | 19.4 | 15.7 | 111 | | Focal | 400 | 35.9 | 19.4 | 8.0 | 21.1 | **16.1** | 112 | | Focal | 600 | 29.2 | 17.6 | 8.7 | 18.5 | 14.0 | 113 | | Focal | 800 | 31.9 | 20.1 | 8.7 | 20.2 | 12.4 | 114 | | OHEM | 200 | 35.1 | 18.7 | 6.3 | 20.0 | 11.3 | 115 | | OHEM | 400 | 33.9 | 19.2 | 7.4 | 20.2 | 13.6 | 116 | | OHEM | 600 | 37.6 | 23.4 | 11.2 | 24.1 | 12.5 | 117 | | OHEM | 800 | **36.8** | **24.3** | **12.2** | **24.4** | 12.3| 118 | 119 | Results of the baseline models with different loss function, backbone networks, where input image size is fixed to 400. **AP_10**, **AP_30**, **AP_50**, and **AP_mean** are presented on validation-set, while **Test-AP_mean** is computed based on test-set similar to **AP_mean**. 120 | 121 | | Loss | Network | AP_10 | AP_30 | AP_50 | AP_MEAN | Test-AP_MEAN | 122 | |----- |----- |:-------: |:--------:| :-----: | :-------:| :------: | 123 | | Focal | ResNet18 | 35.1 | 18.9 | 8.1 | 20.7 | **15.3** | 124 | | OHEM | ResNet18 | 36.0 | **20.7** | 7.7 | **21.5** | 13.8 | 125 | | Focal | ResNet34 | 34.6 | 18.9 | 6.4 | 19.9 | 14.3 | 126 | | OHEM | ResNet34 | **36.7** | 20.4 | 7.1 | 21.4 | 13.8 | 127 | | Focal | ResNet50 | 35.9 | 19.4 | **8.0** | 21.1 | 16.1 | 128 | | OHEM | ResNet50 | 33.9 | 19.2 | 7.4 | 20.2 | 13.6 | 129 | | Focal | ResNet101 | 32.5 | 17.2 | 6.1 | 18.6 | 14.0 | 130 | | OHEM | ResNet101 | 36.6 | 20.1 | 7.4 | 21.3 | 12.3 | 131 | 132 | Outputs from the lastest model (800 OHEM) are uploaded in the sample folder. These are generated using the same model (800 OHEM). See flag at line [evaluate.py 114](https://github.com/Viveksbawa/SARAS-ESAD-Baseline/blob/master/evaluate.py#L114) to select validation or testing set (which will be available on 10th June). 133 | 134 | ## Details 135 | - Input image size (`height x width`)is `600x1067` or `800x1422`. 136 | - Batch size is set to `16`, the learning rate of `0.01`. 137 | - Weights for initial layers are frozen see `freezeupto` flag in `train.py` 138 | - max number of iterations is set to 6000 139 | - SGD is used for the optimisation 140 | - initial learning rate is set to `0.01` 141 | - learning rate is dropped by the factor of 10 after 5000 iterations 142 | - Different training setting might result in better/same/worse performance 143 | -------------------------------------------------------------------------------- /modules/detection_loss.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Copyright (c) 2019 Gurkirt Singh 4 | All Rights Reserved. 5 | 6 | """ 7 | 8 | import torch.nn as nn 9 | import torch.nn.functional as F 10 | import torch, pdb, time 11 | from modules import box_utils 12 | 13 | 14 | def sigmoid_focal_loss(preds, labels, num_pos, alpha, gamma): 15 | '''Args:: 16 | preds: sigmoid activated predictions 17 | labels: one hot encoded labels 18 | num_pos: number of positve samples 19 | alpha: weighting factor to baclence +ve and -ve 20 | gamma: Exponent factor to baclence easy and hard examples 21 | Return:: 22 | loss: computed loss and reduced by sum and normlised by num_pos 23 | ''' 24 | loss = F.binary_cross_entropy(preds, labels, reduction='none') 25 | alpha_factor = alpha * labels + (1.0 - alpha) * (1.0 - labels) 26 | pt = preds * labels + (1.0 - preds) * (1.0 - labels) 27 | focal_weight = alpha_factor * ((1-pt) ** gamma) 28 | loss = (loss * focal_weight).sum() / num_pos 29 | return loss 30 | 31 | 32 | # Credits:: from https://github.com/facebookresearch/maskrcnn-benchmark/blob/master/maskrcnn_benchmark/layers/smooth_l1_loss.py 33 | # smooth l1 with beta 34 | def smooth_l1_loss(input, target, beta=1. / 9, reduction='sum'): 35 | n = torch.abs(input - target) 36 | cond = n < beta 37 | loss = torch.where(cond, 0.5 * n ** 2 / beta, n - 0.5 * beta) 38 | if reduction == 'mean': 39 | return loss.mean() 40 | return loss.sum() 41 | 42 | # Credits:: https://github.com/amdegroot/ssd.pytorch for multi-box loss 43 | # https://github.com/qfgaohao/pytorch-ssd/blob/master/vision/nn/multibox_loss.py pytorch1.x mulitbox loss 44 | # adopated by Gurkirt Singh 45 | class MultiBoxLoss(nn.Module): 46 | def __init__(self, positive_threshold, neg_pos_ratio=3): 47 | """ 48 | 49 | Implement SSD Multibox Loss. 50 | Basically, Multibox loss combines classification loss 51 | and Smooth L1 regression loss. 52 | 53 | """ 54 | super(MultiBoxLoss, self).__init__() 55 | self.positive_threshold = positive_threshold 56 | self.neg_pos_ratio = neg_pos_ratio 57 | 58 | def forward(self, confidence, predicted_locations, gts, counts, anchors): 59 | 60 | 61 | """ 62 | 63 | Compute classification loss and smooth l1 loss. 64 | Args: 65 | confidence (batch_size, num_anchors, num_classes): class predictions. 66 | locations (batch_size, num_anchors, 4): predicted locations. 67 | boxes list of len = batch_size and nx4 arrarys 68 | anchors: (num_anchors, 4) 69 | 70 | """ 71 | 72 | num_classes = confidence.size(2) 73 | gt_locations = [] 74 | labels = [] 75 | with torch.no_grad(): 76 | # torch.cuda.synchronize() 77 | # t0 = time.perf_counter() 78 | for b in range(len(gts)): 79 | gt_boxes = gts[b, :counts[b], :4] 80 | gt_labels = gts[b, :counts[b], 4] 81 | gt_labels = gt_labels.type(torch.cuda.LongTensor) 82 | 83 | conf, loc = box_utils.match_anchors(gt_boxes, gt_labels, anchors, iou_threshold=self.positive_threshold) 84 | 85 | labels.append(conf) 86 | gt_locations.append(loc) 87 | gt_locations = torch.stack(gt_locations, 0) 88 | labels = torch.stack(labels, 0) 89 | loss = -F.log_softmax(confidence, dim=2)[:, :, 0] 90 | mask = box_utils.hard_negative_mining(loss, labels, self.neg_pos_ratio) 91 | 92 | # pdb.set_trace() 93 | pos_mask = labels > 0 94 | num_pos = max(1.0, float(pos_mask.sum())) 95 | 96 | confidence = confidence[mask, :] 97 | classification_loss = F.cross_entropy(confidence.reshape(-1, num_classes), labels[mask], reduction='sum') / (num_pos * 4.0) 98 | 99 | predicted_locations = predicted_locations[pos_mask, :].reshape(-1, 4) 100 | gt_locations = gt_locations[pos_mask, :].reshape(-1, 4) 101 | 102 | localisation_loss = F.smooth_l1_loss(predicted_locations, gt_locations, reduction='sum')/(num_pos * 4.0) 103 | 104 | return localisation_loss, classification_loss 105 | 106 | 107 | class YOLOLoss(nn.Module): 108 | def __init__(self, positive_threshold, negative_threshold): 109 | """Implement YOLO Loss. 110 | Basically, combines yolo classification loss 111 | and Smooth L1 regression loss. 112 | """ 113 | super(YOLOLoss, self).__init__() 114 | self.positive_threshold = positive_threshold 115 | self.negative_threshold =negative_threshold 116 | self.bce_loss = nn.BCELoss().cuda() 117 | self.pos_weight = 1.0 118 | self.neg_weight = 0.5 119 | 120 | 121 | def forward(self, confidence, predicted_locations, gts, counts, anchors): 122 | """ 123 | Compute classification loss and smooth l1 loss. 124 | Args: 125 | confidence (batch_size, num_anchors, num_classes): class predictions. 126 | locations (batch_size, num_anchors, 4): predicted locations. 127 | boxes list of len = batch_size and nx4 arrarys 128 | anchors: (num_anchors, 4) 129 | 130 | """ 131 | 132 | 133 | confidence = torch.sigmoid(confidence) 134 | binary_preds = confidence[:,:, 0] 135 | object_preds = confidence[:,:,1:] 136 | num_classes = object_preds.size(2) 137 | N = float(len(gts)) 138 | gt_locations = [] 139 | labels = [] 140 | labels_bin = [] 141 | with torch.no_grad(): 142 | # torch.cuda.synchronize() 143 | # t0 = time.perf_counter() 144 | for b in range(len(gts)): 145 | # gt_boxes = gts[b][:,:4] 146 | # gt_labels = gts[b][:,4] 147 | gt_boxes = gts[b, :counts[b], :4] 148 | gt_labels = gts[b, :counts[b], 4] 149 | gt_labels = gt_labels.type(torch.cuda.LongTensor) 150 | 151 | conf, loc = box_utils.match_anchors_wIgnore(gt_boxes, gt_labels, anchors, 152 | pos_th=self.positive_threshold, nge_th=self.negative_threshold ) 153 | 154 | gt_locations.append(loc) 155 | 156 | y_onehot = object_preds.new_zeros(conf.size(0), num_classes+1) 157 | pos_conf = conf.clone() 158 | pos_conf[pos_conf<0] = 0 # make ingonre bg 159 | y_onehot[range(y_onehot.shape[0]), pos_conf] = 1.0 160 | labels.append(y_onehot[:,1:]) 161 | labels_bin.append(conf) 162 | 163 | gt_locations = torch.stack(gt_locations, 0) 164 | labels = torch.stack(labels, 0) 165 | labels_bin = torch.stack(labels_bin, 0) 166 | 167 | pos_mask = labels_bin > 0 168 | num_pos = max(1.0, float(pos_mask.sum())) 169 | 170 | predicted_locations = predicted_locations[pos_mask].reshape(-1, 4) 171 | gt_locations = gt_locations[pos_mask].reshape(-1, 4) 172 | localisation_loss = smooth_l1_loss(predicted_locations, gt_locations, reduction='sum')/(num_pos * 4.0) 173 | 174 | # mask = labels_bin > -1 # Get mask to remove ignore examples 175 | object_preds = object_preds[pos_mask].reshape(-1,num_classes) # Remove Ignore preds 176 | labels = labels[pos_mask].reshape(-1, num_classes) # Remove Ignore labels 177 | # pdb.set_trace() 178 | classification_loss = F.binary_cross_entropy(object_preds, labels, reduction='sum')/num_pos 179 | 180 | labels_bin = labels_bin.float() 181 | labels_bin[labels_bin>0] = 1.0 182 | neg_mask = labels_bin==0 183 | 184 | binary_loss_pos = F.binary_cross_entropy(binary_preds[pos_mask], labels_bin[pos_mask], reduction='sum') 185 | binary_loss_neg = F.binary_cross_entropy(binary_preds[neg_mask], labels_bin[neg_mask], reduction='sum') 186 | 187 | binary_loss = (binary_loss_pos*self.pos_weight + binary_loss_neg*self.neg_weight)/num_pos 188 | 189 | # print(classification_loss, binary_loss) 190 | return localisation_loss, (classification_loss + binary_loss)/2.0 191 | 192 | 193 | class FocalLoss(nn.Module): 194 | def __init__(self, positive_threshold, negative_threshold, alpha=0.25, gamma=2.0): 195 | """Implement YOLO Loss. 196 | Basically, combines focal classification loss 197 | and Smooth L1 regression loss. 198 | """ 199 | super(FocalLoss, self).__init__() 200 | self.positive_threshold = positive_threshold 201 | self.negative_threshold = negative_threshold 202 | # self.bce_loss = nn.BCELoss(reduction='sum').cuda() 203 | self.alpha = 0.25 204 | self.gamma = 2.0 205 | 206 | 207 | def forward(self, confidence, predicted_locations, gts, counts, anchors): 208 | 209 | """ 210 | 211 | Compute classification loss and smooth l1 loss. 212 | Args: 213 | confidence (batch_size, num_anchors, num_classes): class predictions. 214 | locations (batch_size, num_anchors, 4): predicted locations. 215 | boxes list of len = batch_size and nx4 arrarys 216 | anchors: (num_anchors, 4) 217 | 218 | """ 219 | 220 | confidence = torch.sigmoid(confidence) 221 | binary_preds = confidence[:,:, 0] 222 | object_preds = confidence[:,:,1:] 223 | num_classes = object_preds.size(2) 224 | N = float(len(gts)) 225 | gt_locations = [] 226 | labels = [] 227 | labels_bin = [] 228 | with torch.no_grad(): 229 | # torch.cuda.synchronize() 230 | # t0 = time.perf_counter() 231 | for b in range(len(gts)): 232 | gt_boxes = gts[b, :counts[b], :4] 233 | gt_labels = gts[b, :counts[b], 4] 234 | gt_labels = gt_labels.type(torch.cuda.LongTensor) 235 | 236 | conf, loc = box_utils.match_anchors_wIgnore(gt_boxes, gt_labels, anchors, pos_th=self.positive_threshold, nge_th=self.negative_threshold ) 237 | 238 | gt_locations.append(loc) 239 | 240 | y_onehot = object_preds.new_zeros(conf.size(0), num_classes+1) 241 | pos_conf = conf.clone() 242 | pos_conf[pos_conf<0] = 0 # make ingonre bg 243 | y_onehot[range(y_onehot.shape[0]), pos_conf] = 1.0 244 | labels.append(y_onehot[:,1:]) 245 | labels_bin.append(conf) 246 | 247 | gt_locations = torch.stack(gt_locations, 0) 248 | labels = torch.stack(labels, 0) 249 | labels_bin = torch.stack(labels_bin, 0) 250 | 251 | pos_mask = labels_bin > 0 252 | num_pos = max(1.0, float(pos_mask.sum())) 253 | 254 | predicted_locations = predicted_locations[pos_mask].reshape(-1, 4) 255 | gt_locations = gt_locations[pos_mask].reshape(-1, 4) 256 | localisation_loss = smooth_l1_loss(predicted_locations, gt_locations, reduction='sum')/(num_pos * 4.0) 257 | 258 | mask = labels_bin > -1 # Get mask to remove ignore examples 259 | object_preds = object_preds[mask].reshape(-1,num_classes) # Remove Ignore preds 260 | labels = labels[mask].reshape(-1,num_classes) # Remove Ignore labels 261 | 262 | classification_loss = sigmoid_focal_loss(object_preds, labels, num_pos, self.alpha, self.gamma) 263 | 264 | labels_bin[labels_bin>0] = 1 265 | binary_preds = binary_preds[labels_bin>-1] 266 | labels_bin = labels_bin[labels_bin>-1] 267 | binary_loss = sigmoid_focal_loss(binary_preds.float(), labels_bin.float(), num_pos, self.alpha, self.gamma) 268 | 269 | return localisation_loss, (classification_loss + binary_loss)/2.0 270 | -------------------------------------------------------------------------------- /evaluate.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | 4 | Adapted from: 5 | Modification by: Gurkirt Singh 6 | Modification started: 2nd April 2019 7 | large parts of this files are from many github repos 8 | mainly adopted from 9 | https://github.com/gurkirt/realtime-action-detection 10 | 11 | Please don't remove above credits and give star to these repos 12 | Licensed under The MIT License [see LICENSE for details] 13 | 14 | """ 15 | 16 | import os 17 | import pdb 18 | import time, json 19 | import socket 20 | import getpass 21 | import argparse 22 | import datetime 23 | import numpy as np 24 | import torch 25 | import torch.nn as nn 26 | import torch.optim as optim 27 | import torch.utils.data as data_utils 28 | from torch.optim.lr_scheduler import MultiStepLR 29 | from modules import utils 30 | from modules.utils import str2bool 31 | from modules.evaluation import evaluate_detections 32 | from modules.box_utils import decode, nms 33 | from modules import AverageMeter 34 | from data import DetectionDataset, custum_collate 35 | from models.retinanet_shared_heads import build_retinanet_shared_heads 36 | from torchvision import transforms 37 | from data.transforms import Resize 38 | 39 | parser = argparse.ArgumentParser(description='Training single stage FPN with OHEM, resnet as backbone') 40 | # Name of backbone networ, e.g. resnet18, resnet34, resnet50, resnet101 resnet152 are supported 41 | parser.add_argument('--basenet', default='resnet50', help='pretrained base model') 42 | # if output heads are have shared features or not: 0 is no-shareing else sharining enabled 43 | parser.add_argument('--multi_scale', default=False, type=str2bool,help='perfrom multiscale training') 44 | parser.add_argument('--shared_heads', default=0, type=int,help='4 head layers') 45 | parser.add_argument('--num_head_layers', default=4, type=int,help='0 mean no shareding more than 0 means shareing') 46 | parser.add_argument('--use_bias', default=True, type=str2bool,help='0 mean no bias in head layears') 47 | # Name of the dataset only esad is supported 48 | parser.add_argument('--dataset', default='esad', help='pretrained base model') 49 | # Input size of image only 600 is supprted at the moment 50 | parser.add_argument('--min_size', default=600, type=int, help='Input Size for FPN') 51 | parser.add_argument('--max_size', default=1080, type=int, help='Input Size for FPN') 52 | # data loading argumnets 53 | parser.add_argument('--batch_size', default=16, type=int, help='Batch size for training') 54 | # Number of worker to load data in parllel 55 | parser.add_argument('--num_workers', '-j', default=8, type=int, help='Number of workers used in dataloading') 56 | # optimiser hyperparameters 57 | parser.add_argument('--optim', default='SGD', type=str, help='Optimiser type') 58 | parser.add_argument('--loss_type', default='mbox', type=str, help='loss_type') 59 | parser.add_argument('--lr', '--learning-rate', default=0.01, type=float, help='initial learning rate') 60 | parser.add_argument('--eval_iters', default='5000,6000,7000,9000', type=str, help='Chnage the lr @') 61 | 62 | # Freeze batch normlisatio layer or not 63 | parser.add_argument('--fbn', default=True, type=bool, help='if less than 1 mean freeze or else any positive values keep updating bn layers') 64 | parser.add_argument('--freezeupto', default=1, type=int, help='if 0 freeze or else keep updating bn layers') 65 | 66 | # Evaluation hyperparameters 67 | parser.add_argument('--iou_threshs', default='', type=str, help='Evaluation thresholds') 68 | parser.add_argument('--conf_thresh', default=0.05, type=float, help='Confidence threshold for evaluation') 69 | parser.add_argument('--nms_thresh', default=0.5, type=float, help='NMS threshold') 70 | parser.add_argument('--topk', default=25, type=int, help='topk for evaluation') 71 | 72 | # Progress logging 73 | parser.add_argument('--log_iters', default=True, type=str2bool, help='Print the loss at each iteration') 74 | parser.add_argument('--log_step', default=10, type=int, help='Log after k steps for text/tensorboard') 75 | parser.add_argument('--tensorboard', default=False, type=str2bool, help='Use tensorboard for loss/evalaution visualization') 76 | 77 | # Program arguments 78 | parser.add_argument('--man_seed', default=1, type=int, help='manualseed for reproduction') 79 | parser.add_argument('--multi_gpu', default=1, type=int, help='If more than then use all visible GPUs by default only one GPU used ') 80 | 81 | # source or dstination directories 82 | parser.add_argument('--data_root', default='/mnt/mercury-fast/datasets/', help='Location to root directory fo dataset') # /mnt/mars-fast/datasets/ 83 | parser.add_argument('--save_root', default='/mnt/mercury-alpha/', help='Location to save checkpoint models') # /mnt/sun-gamma/datasets/ 84 | parser.add_argument('--model_dir', default='/mnt/mars-gamma/global-models/pytorch-imagenet/', help='Location to where imagenet pretrained models exists') # /mnt/mars-fast/datasets/ 85 | 86 | 87 | ## Parse arguments 88 | args = parser.parse_args() 89 | 90 | args = utils.set_args(args, 'test') # set directories and subsets fo datasets 91 | 92 | ## set random seeds and global settings 93 | np.random.seed(args.man_seed) 94 | torch.manual_seed(args.man_seed) 95 | torch.cuda.manual_seed_all(args.man_seed) 96 | torch.set_default_tensor_type('torch.FloatTensor') 97 | 98 | 99 | def main(): 100 | 101 | args.exp_name = utils.create_exp_name(args) 102 | 103 | args.data_root += args.dataset+'/' 104 | args.save_root += args.dataset+'/' 105 | args.save_root += 'cache/'+args.exp_name+'/' 106 | 107 | 108 | val_transform = transforms.Compose([ 109 | Resize(args.min_size, args.max_size), 110 | transforms.ToTensor(), 111 | transforms.Normalize(mean=args.means,std=args.stds)]) 112 | if False: # while validating 113 | val_dataset = DetectionDataset(root= args.data_root, train=False, input_sets=['val/obj'], transform=val_transform, full_test=False) 114 | else: # while testing 115 | val_dataset = DetectionDataset(root= args.data_root, train=False, input_sets=['testC'], transform=val_transform, full_test=True) 116 | 117 | print('Done Loading Dataset Validation Dataset :::>>>\n',val_dataset.print_str) 118 | 119 | args.data_dir = val_dataset.root 120 | args.num_classes = len(val_dataset.classes) + 1 121 | args.classes = val_dataset.classes 122 | args.head_size = 256 123 | 124 | net = build_retinanet_shared_heads(args).cuda() 125 | 126 | if args.multi_gpu>0: 127 | print('\nLets do dataparallel\n') 128 | net = torch.nn.DataParallel(net) 129 | net.eval() 130 | 131 | if len(args.iou_threshs)>2: 132 | args.iou_threshs = [float(th) for th in args.iou_threshs.split(',')] 133 | else: 134 | args.iou_threshs = [th for th in np.arange(0.05,0.951,0.05)] 135 | 136 | overal_json_object = {} 137 | for iteration in args.eval_iters: 138 | args.det_itr = iteration 139 | args.model_path = args.save_root + 'model_{:06d}.pth'.format(iteration) 140 | 141 | if not os.path.isfile(args.model_path): 142 | continue 143 | 144 | log_file = open("{pt:s}/testing-{it:06d}.log".format(pt=args.save_root, it=iteration), "w", 1) 145 | log_file.write(args.exp_name + '\n') 146 | submission_file = open("{pt:s}/submission.txt".format(pt=args.save_root), "w", 1) 147 | log_file.write(args.model_path+'\n') 148 | 149 | net.load_state_dict(torch.load(args.model_path)) 150 | 151 | print('Finished loading model %d !' % iteration) 152 | # Load dataset 153 | val_data_loader = data_utils.DataLoader(val_dataset, int(args.batch_size), num_workers=args.num_workers, 154 | shuffle=False, pin_memory=True, collate_fn=custum_collate) 155 | 156 | # evaluation 157 | torch.cuda.synchronize() 158 | tt0 = time.perf_counter() 159 | log_file.write('Testing net \n') 160 | net.eval() # switch net to evaluation mode 161 | 162 | 163 | # print(args.iou_threshs) 164 | result_list = validate(args, net, val_data_loader, val_dataset, iteration, submission_file, args.iou_threshs) 165 | 166 | for result in result_list: 167 | [iou_thresh, mAP, ap_all, ap_strs] = result 168 | 169 | ptr_str = '\n\nIOU Threshold: {:0.2f}:: \n'.format(iou_thresh) 170 | print(ptr_str) 171 | log_file.write(ptr_str) 172 | 173 | for ap_str in ap_strs: 174 | print(ap_str) 175 | log_file.write(ap_str+'\n') 176 | 177 | ptr_str = '\nMEANAP:::=>{:0.4f}\n'.format(mAP*100) 178 | print(ptr_str) 179 | log_file.write(ptr_str) 180 | 181 | ptr_str = 'printing the on mAP results again \n' 182 | print(ptr_str) 183 | log_file.write(ptr_str) 184 | 185 | thmap_dict = {'30':0,'10':0,'50':0,'mean':0} 186 | summap = 0 187 | for result in result_list: 188 | [iou_thresh, mAP, ap_all, ap_strs] = result 189 | thstr = str(int(iou_thresh*100)) 190 | if thstr in thmap_dict: 191 | thmap_dict[thstr] = mAP*100 192 | summap += mAP*100 193 | ptr_str = '\nIOUTH : mAP :: {:0.2f} : {:0.2f}\n'.format(iou_thresh,mAP*100) 194 | print(ptr_str) 195 | log_file.write(ptr_str) 196 | 197 | thmap_dict['mean'] = summap/3.0 198 | overal_json_object[str(int(iteration))] = thmap_dict 199 | torch.cuda.synchronize() 200 | print('Complete set time {:0.2f}'.format(time.perf_counter() - tt0)) 201 | log_file.close() 202 | 203 | result_name = 'results/test-frameAP-{:s}-{:s}-{:d}'.format(args.loss_type, args.basenet, args.min_size) 204 | 205 | with open(result_name+'.json', 'w') as f: 206 | json.dump(overal_json_object,f) 207 | 208 | fid = open(result_name+'.txt', 'w') 209 | fid.write('{:s} {:s} {:d}\n'.format(args.loss_type, args.basenet, args.min_size)) 210 | for iter in sorted(overal_json_object.keys()): 211 | res = overal_json_object[iter] 212 | fid.write('{:s} {:0.1f} {:0.1f} {:0.1f} {:0.1f}\n'.format(iter, res['10'], res['30'], res['50'], res['mean'])) 213 | fid.close() 214 | 215 | 216 | 217 | def validate(args, net, val_data_loader, val_dataset, iteration_num, submission_file, iou_threshs=[0.2,0.25,0.3,0.4,0.5,0.75]): 218 | """Test a FPN network on an image database.""" 219 | print('Validating at ', iteration_num) 220 | num_images = len(val_dataset) 221 | num_classes = args.num_classes 222 | 223 | det_boxes = [[] for _ in range(num_classes-1)] 224 | gt_boxes = [] 225 | print_time = True 226 | val_step = 20 227 | count = 0 228 | torch.cuda.synchronize() 229 | ts = time.perf_counter() 230 | activation = nn.Sigmoid().cuda() 231 | if args.loss_type == 'mbox': 232 | activation = nn.Softmax(dim=2).cuda() 233 | 234 | dict_for_json_dump = {} 235 | 236 | with torch.no_grad(): 237 | for val_itr, (images, targets, batch_counts, img_indexs, wh) in enumerate(val_data_loader): 238 | 239 | torch.cuda.synchronize() 240 | t1 = time.perf_counter() 241 | 242 | batch_size = images.size(0) 243 | 244 | images = images.cuda(0, non_blocking=True) 245 | decoded_boxes, conf_data = net(images) 246 | 247 | conf_scores_all = activation(conf_data).clone() 248 | 249 | if print_time and val_itr%val_step == 0: 250 | torch.cuda.synchronize() 251 | tf = time.perf_counter() 252 | print('Forward Time {:0.3f}'.format(tf-t1)) 253 | 254 | for b in range(batch_size): 255 | image_path = val_dataset.ids[img_indexs[b]][0] 256 | image_name = image_path.split('/')[-1] 257 | # print(image_name) 258 | width, height = wh[b][0], wh[b][1] 259 | gt = targets[b, :batch_counts[b]].numpy() 260 | gt_boxes.append(gt) 261 | # decoded_boxes = decode(loc_data[b], anchors).clone() 262 | conf_scores = conf_scores_all[b] 263 | #Apply nms per class and obtain the results 264 | decoded_boxes_b = decoded_boxes[b] 265 | for cl_ind in range(1, num_classes): 266 | # pdb.set_trace() 267 | scores = conf_scores[:, cl_ind].squeeze() 268 | if args.loss_type == 'yolo': 269 | scores = conf_scores[:, cl_ind].squeeze() * conf_scores[:, 0].squeeze() * 5.0 270 | c_mask = scores.gt(args.conf_thresh) # greater than minmum threshold 271 | scores = scores[c_mask].squeeze() 272 | # print('scores size',c_mask.sum()) 273 | if scores.dim() == 0: 274 | # print(len(''), ' dim ==0 ') 275 | det_boxes[cl_ind - 1].append(np.asarray([])) 276 | continue 277 | # boxes = decoded_boxes_b.clone() 278 | l_mask = c_mask.unsqueeze(1).expand_as(decoded_boxes_b) 279 | boxes = decoded_boxes_b[l_mask].clone().view(-1, 4) 280 | # idx of highest scoring and non-overlapping boxes per class 281 | ids, counts = nms(boxes, scores, args.nms_thresh, args.topk*20) # idsn - ids after nms 282 | scores = scores[ids[:min(args.topk,counts)]].cpu().numpy() 283 | # pick = min(scores.shape[0], 20) 284 | # scores = scores[:pick] 285 | boxes = boxes[ids[:min(args.topk,counts)]].cpu().numpy() 286 | 287 | for ik in range(boxes.shape[0]): 288 | boxes[ik, 0] = max(0, boxes[ik, 0]) 289 | boxes[ik, 2] = min(width, boxes[ik, 2]) 290 | boxes[ik, 1] = max(0, boxes[ik, 1]) 291 | boxes[ik, 3] = min(height, boxes[ik, 3]) 292 | write_string = '{:s} {:0.6f} {:0.6f} {:0.6f} {:0.6f} {:0.6f} {:d}\n'.format(image_name, 293 | boxes[ik, 0]/width, boxes[ik, 1]/height, boxes[ik, 2]/width, boxes[ik, 3]/height, scores[ik], cl_ind-1) 294 | submission_file.write(write_string) 295 | 296 | cls_dets = np.hstack((boxes, scores[:, np.newaxis])).astype(np.float32, copy=True) 297 | det_boxes[cl_ind-1].append(cls_dets) 298 | count += 1 299 | 300 | if print_time and val_itr%val_step == 0: 301 | torch.cuda.synchronize() 302 | te = time.perf_counter() 303 | print('im_detect: {:d}/{:d} time taken {:0.3f}'.format(count, num_images, te-ts)) 304 | torch.cuda.synchronize() 305 | ts = time.perf_counter() 306 | if print_time and val_itr%val_step == 0: 307 | torch.cuda.synchronize() 308 | te = time.perf_counter() 309 | print('NMS stuff Time {:0.3f}'.format(te - tf)) 310 | 311 | 312 | submission_file.close() 313 | print('Evaluating detections for itration number ', iteration_num) 314 | return_list = [] 315 | for iou_thresh in iou_threshs: 316 | mAP, ap_all, ap_strs , _ = evaluate_detections(gt_boxes, det_boxes, val_dataset.classes, iou_thresh=iou_thresh) 317 | return_list.append([iou_thresh, mAP, ap_all, ap_strs]) 318 | return return_list 319 | 320 | if __name__ == '__main__': 321 | main() 322 | -------------------------------------------------------------------------------- /modules/box_utils.py: -------------------------------------------------------------------------------- 1 | import torch, pdb, math 2 | import numpy as np 3 | 4 | def match_anchors(gt_boxes, gt_labels, anchors, iou_threshold=0.5, variances=[0.1, 0.2], seq_len=1): 5 | # pdb.set_trace() 6 | # pdb.set_trace() 7 | num_mt = int(gt_labels.size(0)/seq_len) 8 | # pdb.set_trace() 9 | seq_overlaps =[] 10 | inds = torch.LongTensor([m*seq_len for m in range(num_mt)]) 11 | # print(inds, num_mt) 12 | ## get indexes of first frame in seq for each microtube 13 | gt_labels = gt_labels[inds] 14 | for s in range(seq_len): 15 | seq_overlaps.append(jaccard(gt_boxes[inds+s, :], anchors)) 16 | 17 | overlaps = seq_overlaps[0] 18 | # print(overlaps.max()) 19 | ## Compute average overlap 20 | for s in range(seq_len-1): 21 | overlaps = overlaps + seq_overlaps[s+1] 22 | overlaps = overlaps/float(seq_len) 23 | 24 | # (Bipartite Matching) 25 | # [1,num_objects] best anchor for each ground truth 26 | best_anchor_overlap, best_anchor_idx = overlaps.max(1, keepdim=True) 27 | # [1,num_anchors] best ground truth for each anchor 28 | best_truth_overlap, best_truth_idx = overlaps.max(0, keepdim=True) 29 | best_truth_idx.squeeze_(0) 30 | best_truth_overlap.squeeze_(0) 31 | best_anchor_idx.squeeze_(1) 32 | best_anchor_overlap.squeeze_(1) 33 | best_truth_overlap.index_fill_(0, best_anchor_idx, 2) # ensure best anchor 34 | # ensure every gt matches with its anchor of max overlap 35 | # pdb.set_trace() 36 | for j in range(best_anchor_idx.size(0)): 37 | best_truth_idx[best_anchor_idx[j]] = j 38 | 39 | conf = gt_labels[best_truth_idx] + 1 # Shape: [num_anchors] 40 | conf[best_truth_overlap < iou_threshold] = 0 # label as background 41 | 42 | for s in range(seq_len): 43 | st = gt_boxes[inds + s, :] 44 | matches = st[best_truth_idx] # Shape: [num_anchors,4] 45 | if s == 0: 46 | loc = encode(matches, anchors[:, s * 4:(s + 1) * 4], variances) 47 | # Shape: [num_anchors, 4] -- encode the gt boxes for frame i 48 | else: 49 | temp = encode(matches, anchors[:, s * 4:(s + 1) * 4], variances) 50 | loc = torch.cat([loc, temp], 1) # shape: [num_anchors x 4 * seql_len] : stacking the location targets for different frames 51 | 52 | return conf, loc 53 | 54 | 55 | def match_anchors_wIgnore(gt_boxes, gt_labels, anchors, pos_th=0.5, nge_th=0.4, variances=[0.1, 0.2], seq_len=1): 56 | # pdb.set_trace() 57 | # pdb.set_trace() 58 | num_mt = int(gt_labels.size(0)/seq_len) 59 | 60 | # pdb.set_trace() 61 | seq_overlaps =[] 62 | inds = torch.LongTensor([m*seq_len for m in range(num_mt)]) 63 | # print(inds, num_mt) 64 | ## get indexes of first frame in seq for each microtube 65 | gt_labels = gt_labels[inds] 66 | for s in range(seq_len): 67 | seq_overlaps.append(jaccard(gt_boxes[inds+s, :], anchors)) 68 | 69 | overlaps = seq_overlaps[0] 70 | # print(overlaps.max()) 71 | ## Compute average overlap 72 | for s in range(seq_len-1): 73 | overlaps = overlaps + seq_overlaps[s+1] 74 | overlaps = overlaps/float(seq_len) 75 | best_anchor_overlap, best_anchor_idx = overlaps.max(1, keepdim=True) 76 | # [1,num_anchors] best ground truth for each anchor 77 | best_truth_overlap, best_truth_idx = overlaps.max(0, keepdim=True) 78 | best_truth_idx.squeeze_(0) 79 | best_truth_overlap.squeeze_(0) 80 | best_anchor_idx.squeeze_(1) 81 | best_anchor_overlap.squeeze_(1) 82 | best_truth_overlap.index_fill_(0, best_anchor_idx, 2) # ensure best anchor 83 | # ensure every gt matches with its anchor of max overlap 84 | for j in range(best_anchor_idx.size(0)): 85 | best_truth_idx[best_anchor_idx[j]] = j 86 | 87 | conf = gt_labels[best_truth_idx] + 1 # assigned nearest class label 88 | conf[best_truth_overlap < pos_th] = -1 # label as ignore 89 | conf[best_truth_overlap < nge_th] = 0 # label as background 90 | 91 | for s in range(seq_len): 92 | st = gt_boxes[inds + s, :] 93 | matches = st[best_truth_idx] # Shape: [num_anchors,4] 94 | if s == 0: 95 | loc = encode(matches, anchors[:, s * 4:(s + 1) * 4], variances) 96 | # Shape: [num_anchors, 4] -- encode the gt boxes for frame i 97 | else: 98 | temp = encode(matches, anchors[:, s * 4:(s + 1) * 4], variances) 99 | loc = torch.cat([loc, temp], 1) # shape: [num_anchors x 4 * seql_len] : stacking the location targets for different frames 100 | 101 | return conf, loc 102 | 103 | 104 | def hard_negative_mining(loss, labels, neg_pos_ratio): 105 | """ 106 | It used to suppress the presence of a large number of negative prediction. 107 | It works on image level not batch level. 108 | For any example/image, it keeps all the positive predictions and 109 | cut the number of negative predictions to make sure the ratio 110 | between the negative examples and positive examples is no more 111 | the given ratio for an image. 112 | Args: 113 | loss (N, num_anchors): the loss for each example. 114 | labels (N, num_anchors): the labels. 115 | neg_pos_ratio: the ratio between the negative examples and positive examples. 116 | 117 | """ 118 | 119 | pos_mask = labels > 0 120 | num_pos = pos_mask.long().sum(dim=1, keepdim=True) 121 | num_neg = num_pos * neg_pos_ratio 122 | 123 | loss[pos_mask] = -math.inf 124 | _, indexes = loss.sort(dim=1, descending=True) 125 | _, orders = indexes.sort(dim=1) 126 | neg_mask = orders < num_neg 127 | return pos_mask | neg_mask 128 | 129 | 130 | def point_form(boxes): 131 | """ Convert anchor_boxes to (xmin, ymin, xmax, ymax) 132 | representation for comparison to point form ground truth data. 133 | Args: 134 | boxes: (tensor) center-size default boxes from anchorbox layers. 135 | Return: 136 | boxes: (tensor) Converted xmin, ymin, xmax, ymax form of boxes. 137 | """ 138 | return torch.cat((boxes[:, :2] - boxes[:, 2:]/2, # xmin, ymin 139 | boxes[:, :2] + boxes[:, 2:]/2), 1) # xmax, ymax 140 | 141 | 142 | def center_size(boxes): 143 | """ Convert anchor_boxes to (cx, cy, w, h) 144 | representation for comparison to center-size form ground truth data. 145 | Args: 146 | boxes: (tensor) point_form boxes 147 | Return: 148 | boxes: (tensor) Converted xmin, ymin, xmax, ymax form of boxes. 149 | """ 150 | return torch.cat((boxes[:, 2:] + boxes[:, :2])/2, # cx, cy 151 | boxes[:, 2:] - boxes[:, :2], 1) # w, h 152 | 153 | 154 | def intersect(box_a, box_b): 155 | """ 156 | 157 | We resize both tensors to [A,B,2] without new malloc: 158 | [A,2] -> [A,1,2] -> [A,B,2] 159 | [B,2] -> [1,B,2] -> [A,B,2] 160 | Then we compute the area of intersect between box_a and box_b. 161 | Args: 162 | box_a: (tensor) bounding boxes, Shape: [A,4]. 163 | box_b: (tensor) bounding boxes, Shape: [B,4]. 164 | Return: 165 | (tensor) intersection area, Shape: [A,B]. 166 | 167 | """ 168 | 169 | A = box_a.size(0) 170 | B = box_b.size(0) 171 | # pdb.set_trace() 172 | # print(box_a.type(), box_b.type()) 173 | max_xy = torch.min(box_a[:, 2:].unsqueeze(1).expand(A, B, 2), 174 | box_b[:, 2:].unsqueeze(0).expand(A, B, 2)) 175 | min_xy = torch.max(box_a[:, :2].unsqueeze(1).expand(A, B, 2), 176 | box_b[:, :2].unsqueeze(0).expand(A, B, 2)) 177 | inter = torch.clamp((max_xy - min_xy), min=0) 178 | return inter[:, :, 0] * inter[:, :, 1] 179 | 180 | 181 | def jaccard(box_a, box_b): 182 | """Compute the jaccard overlap of two sets of boxes. The jaccard overlap 183 | is simply the intersection over union of two boxes. Here we operate on 184 | ground truth boxes and default boxes. 185 | E.g.: 186 | A ∩ B / A ∪ B = A ∩ B / (area(A) + area(B) - A ∩ B) 187 | Args: 188 | box_a: (tensor) Ground truth bounding boxes, Shape: [num_objects,4] 189 | box_b: (tensor) anchor boxes from anchorbox layers, Shape: [num_anchors,4] 190 | Return: 191 | jaccard overlap: (tensor) Shape: [box_a.size(0), box_b.size(0)] 192 | """ 193 | # pdb.set_trace() 194 | inter = intersect(box_a, box_b) 195 | area_a = ((box_a[:, 2]-box_a[:, 0]) * 196 | (box_a[:, 3]-box_a[:, 1])).unsqueeze(1).expand_as(inter) # [A,B] 197 | area_b = ((box_b[:, 2]-box_b[:, 0]) * 198 | (box_b[:, 3]-box_b[:, 1])).unsqueeze(0).expand_as(inter) # [A,B] 199 | union = area_a + area_b - inter 200 | return inter / union # [A,B] 201 | 202 | 203 | def get_ovlp_cellwise(overlaps): 204 | feature_maps = [38, 19, 10, 5, 3, 1] 205 | aratios = [4, 6, 6, 6, 4, 4] 206 | dim = 0 207 | for f in feature_maps: 208 | dim += f*f 209 | out_ovlp = np.zeros(dim) 210 | count = 0 211 | st = 0 212 | for k, f in enumerate(feature_maps): 213 | ar = aratios[k] 214 | for i in range(f*f): 215 | et = st+ar 216 | ovlps_tmp = overlaps[0, st:et] 217 | #pdb.set_trace() 218 | out_ovlp[count] = max(ovlps_tmp) 219 | count += 1 220 | st = et 221 | assert count == dim 222 | 223 | return out_ovlp 224 | 225 | 226 | def encode(matched, anchors, variances): 227 | 228 | """ 229 | 230 | Encode the variances from the anchorbox layers into the ground truth boxes 231 | we have matched (based on jaccard overlap) with the anchor boxes. 232 | Args: 233 | matched: (tensor) Coords of ground truth for each anchor in point-form 234 | Shape: [num_anchors, 4]. 235 | anchors: (tensor) anchor boxes in center-offset form 236 | Shape: [num_anchors,4]. 237 | variances: (list[float]) Variances of anchorboxes 238 | 239 | Return: 240 | encoded boxes (tensor), Shape: [num_anchors, 4] 241 | 242 | """ 243 | 244 | TO_REMOVE = 1 # TODO remove 245 | ex_widths = anchors[:, 2] - anchors[:, 0] + TO_REMOVE 246 | ex_heights = anchors[:, 3] - anchors[:, 1] + TO_REMOVE 247 | ex_ctr_x = anchors[:, 0] + 0.5 * ex_widths 248 | ex_ctr_y = anchors[:, 1] + 0.5 * ex_heights 249 | 250 | gt_widths = matched[:, 2] - matched[:, 0] + TO_REMOVE 251 | gt_heights = matched[:, 3] - matched[:, 1] + TO_REMOVE 252 | gt_ctr_x = matched[:, 0] + 0.5 * gt_widths 253 | gt_ctr_y = matched[:, 1] + 0.5 * gt_heights 254 | 255 | 256 | targets_dx = (gt_ctr_x - ex_ctr_x) / ex_widths / variances[0] 257 | targets_dy = (gt_ctr_y - ex_ctr_y) / ex_heights / variances[0] 258 | targets_dw = torch.log(gt_widths / ex_widths) / variances[1] 259 | targets_dh = torch.log(gt_heights / ex_heights) / variances[1] 260 | 261 | targets = torch.stack((targets_dx, targets_dy, targets_dw, targets_dh), dim=1) 262 | 263 | return targets 264 | 265 | 266 | # Adapted from https://github.com/Hakuyume/chainer-ssd 267 | # def decode(loc, anchors, variances=[0.1, 0.2], bbox_xform_clip=math.log(1000. / 16)): 268 | # """ 269 | # Decode locations from predictions using anchors to undo 270 | # the encoding we did for offset regression at train time. 271 | # Args: 272 | # loc (tensor): location predictions for loc layers, 273 | # Shape: [num_anchors,4] 274 | # anchors (tensor): anchor boxes in center-offset form. 275 | # Shape: [num_anchors,4]. 276 | # variances: (list[float]) Variances of anchorboxes 277 | # Return: 278 | # decoded bounding box predictions 279 | # """ 280 | # #pdb.set_trace() 281 | # TO_REMOVE = 1 # TODO remove 282 | # ex_widths = anchors[:, 2] - anchors[:, 0] + TO_REMOVE 283 | # ex_heights = anchors[:, 3] - anchors[:, 1] + TO_REMOVE 284 | # ex_ctr_x = anchors[:, 0] + 0.5 * ex_widths 285 | # ex_ctr_y = anchors[:, 1] + 0.5 * ex_heights 286 | 287 | # # gt_widths = loc[:, 2] - loc[:, 0] + TO_REMOVE 288 | # # gt_heights = loc[:, 3] - loc[:, 1] + TO_REMOVE 289 | # # gt_ctr_x = matched[:, 0] + 0.5 * gt_widths 290 | # # gt_ctr_y = matched[:, 1] + 0.5 * gt_heights 291 | # # pdb.set_trace() 292 | 293 | # pred_ctr_x = ex_ctr_x + loc[:, 0] * variances[0] * ex_widths 294 | # pred_ctr_y = ex_ctr_y + loc[:, 1] * variances[0] * ex_heights 295 | # pred_width = torch.exp(torch.clamp(loc[:, 2] * variances[1], bbox_xform_clip)) * ex_widths 296 | # pred_height = torch.exp(torch.clamp(loc[:, 3] * variances[1], bbox_xform_clip)) * ex_heights 297 | # boxes = torch.stack((pred_ctr_x, pred_ctr_y, pred_width, pred_height), dim=1) 298 | # boxes[:, :2] -= boxes[:, 2:] / 2 299 | # boxes[:, 2:] += boxes[:, :2] 300 | # return boxes 301 | def decode(loc, anchors, variances=[0.1, 0.2], bbox_xform_clip=math.log(1000. / 16)): 302 | # """ 303 | # Decode locations from predictions using anchors to undo 304 | # the encoding we did for offset regression at train time. 305 | # Args: 306 | # loc (tensor): location predictions for loc layers, 307 | # Shape: [num_anchors,4] 308 | # anchors (tensor): anchor boxes in center-offset form. 309 | # Shape: [num_anchors,4]. 310 | # variances: (list[float]) Variances of anchorboxes 311 | # Return: 312 | # decoded bounding box predictions 313 | # """ 314 | # #pdb.set_trace() 315 | TO_REMOVE = 1 # TODO remove 316 | widths = anchors[:, 2] - anchors[:, 0] + TO_REMOVE 317 | heights = anchors[:, 3] - anchors[:, 1] + TO_REMOVE 318 | ctr_x = anchors[:, 0] + 0.5 * widths 319 | ctr_y = anchors[:, 1] + 0.5 * heights 320 | 321 | wx, wy, ww, wh = 10.0, 10.0, 5.0, 5.0 322 | dx = loc[:, 0::4] * variances[0] 323 | dy = loc[:, 1::4] * variances[0] 324 | dw = loc[:, 2::4] * variances[1] 325 | dh = loc[:, 3::4] * variances[1] 326 | 327 | # Prevent sending too large values into torch.exp() 328 | dw = torch.clamp(dw, max=bbox_xform_clip) 329 | dh = torch.clamp(dh, max=bbox_xform_clip) 330 | 331 | pred_ctr_x = dx * widths[:, None] + ctr_x[:, None] 332 | pred_ctr_y = dy * heights[:, None] + ctr_y[:, None] 333 | pred_w = torch.exp(dw) * widths[:, None] 334 | pred_h = torch.exp(dh) * heights[:, None] 335 | 336 | pred_boxes = torch.zeros_like(loc) 337 | # x1 338 | pred_boxes[:, 0::4] = pred_ctr_x - 0.5 * pred_w 339 | # y1 340 | pred_boxes[:, 1::4] = pred_ctr_y - 0.5 * pred_h 341 | # x2 (note: "- 1" is correct; don't be fooled by the asymmetry) 342 | pred_boxes[:, 2::4] = pred_ctr_x + 0.5 * pred_w - 1 343 | # y2 (note: "- 1" is correct; don't be fooled by the asymmetry) 344 | pred_boxes[:, 3::4] = pred_ctr_y + 0.5 * pred_h - 1 345 | 346 | return pred_boxes 347 | 348 | # Adapted from https://github.com/Hakuyume/chainer-ssd 349 | def decode_seq(loc, anchors, variances, seq_len): 350 | boxes = [] 351 | #print('variances', variances) 352 | for s in range(seq_len): 353 | if s == 0: 354 | boxes = decode(loc[:, :4], anchors[:, :4], variances) 355 | else: 356 | boxes = torch.cat((boxes,decode(loc[:,s*4:(s+1)*4], anchors[:,s*4:(s+1)*4], variances)),1) 357 | 358 | return boxes 359 | 360 | def log_sum_exp(x): 361 | """Utility function for computing log_sum_exp while determining 362 | This will be used to determine unaveraged confidence loss across 363 | all examples in a batch. 364 | Args: 365 | x (Variable(tensor)): conf_preds from conf layers 366 | """ 367 | x_max = x.data.max() 368 | return torch.log(torch.sum(torch.exp(x-x_max), 1, keepdim=True)) + x_max 369 | 370 | 371 | # Original author: Francisco Massa: 372 | # https://github.com/fmassa/object-detection.torch 373 | # Ported to PyTorch by Max deGroot (02/01/2017) 374 | def nms(boxes, scores, overlap=0.5, top_k=20): 375 | """Apply non-maximum suppression at test time to avoid detecting too many 376 | overlapping bounding boxes for a given object. 377 | Args: 378 | boxes: (tensor) The location preds for the img, Shape: [num_anchors,4]. 379 | scores: (tensor) The class predscores for the img, Shape:[num_anchors]. 380 | overlap: (float) The overlap thresh for suppressing unnecessary boxes. 381 | top_k: (int) The Maximum number of box preds to consider. 382 | Return: 383 | The indices of the kept boxes with respect to num_anchors. 384 | """ 385 | 386 | keep = scores.new(scores.size(0)).zero_().long() 387 | if boxes.numel() == 0: 388 | return keep, 0 389 | x1 = boxes[:, 0] 390 | y1 = boxes[:, 1] 391 | x2 = boxes[:, 2] 392 | y2 = boxes[:, 3] 393 | area = torch.mul(x2 - x1, y2 - y1) 394 | v, idx = scores.sort(0) # sort in ascending order 395 | # I = I[v >= 0.01] 396 | idx = idx[-top_k:] # indices of the top-k largest vals 397 | xx1 = boxes.new() 398 | yy1 = boxes.new() 399 | xx2 = boxes.new() 400 | yy2 = boxes.new() 401 | w = boxes.new() 402 | h = boxes.new() 403 | 404 | # keep = torch.Tensor() 405 | count = 0 406 | while idx.numel() > 0: 407 | i = idx[-1] # index of current largest val 408 | # keep.append(i) 409 | keep[count] = i 410 | count += 1 411 | if idx.size(0) == 1: 412 | break 413 | idx = idx[:-1] # remove kept element from view 414 | # load bboxes of next highest vals 415 | torch.index_select(x1, 0, idx, out=xx1) 416 | torch.index_select(y1, 0, idx, out=yy1) 417 | torch.index_select(x2, 0, idx, out=xx2) 418 | torch.index_select(y2, 0, idx, out=yy2) 419 | # store element-wise max with next highest score 420 | xx1 = torch.clamp(xx1, min=x1[i]) 421 | yy1 = torch.clamp(yy1, min=y1[i]) 422 | xx2 = torch.clamp(xx2, max=x2[i]) 423 | yy2 = torch.clamp(yy2, max=y2[i]) 424 | w.resize_as_(xx2) 425 | h.resize_as_(yy2) 426 | w = xx2 - xx1 427 | h = yy2 - yy1 428 | # check sizes of xx1 and xx2.. after each iteration 429 | w = torch.clamp(w, min=0.0) 430 | h = torch.clamp(h, min=0.0) 431 | inter = w*h 432 | # IoU = i / (area(a) + area(b) - i) 433 | rem_areas = torch.index_select(area, 0, idx) # load remaining areas) 434 | union = (rem_areas - inter) + area[i] 435 | IoU = inter/union # store result in iou 436 | # keep only elements with an IoU <= overlap 437 | idx = idx[IoU.le(overlap)] 438 | return keep, count 439 | -------------------------------------------------------------------------------- /train.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Author: Vivek Singh and Gurkirt Singh 4 | DATE: April 2020 5 | 6 | Parts of this files are from many github repos 7 | 8 | @gurkirt mostly from https://github.com/gurkirt/RetinaNet.pytorch.1.x 9 | 10 | @longcw faster_rcnn_pytorch: https://github.com/longcw/faster_rcnn_pytorch 11 | @rbgirshick py-faster-rcnn https://github.com/rbgirshick/py-faster-rcnn 12 | Which was adopated by: Ellis Brown, Max deGroot 13 | https://github.com/amdegroot/ssd.pytorch 14 | 15 | mainly adopted from 16 | https://github.com/gurkirt/realtime-action-detection 17 | 18 | maybe more but that is where I got these from 19 | 20 | Please don't remove above credits and give star to these repos 21 | Licensed under The MIT License [see LICENSE for details] 22 | 23 | """ 24 | 25 | import os 26 | import time 27 | import socket 28 | import getpass 29 | import argparse 30 | import datetime 31 | import pdb 32 | import numpy as np 33 | import torch 34 | import torch.nn as nn 35 | import torch.utils.data as data_utils 36 | from modules.solver import get_optim 37 | from modules import utils 38 | from modules.detection_loss import MultiBoxLoss, YOLOLoss, FocalLoss 39 | from modules.evaluation import evaluate_detections 40 | from modules.box_utils import decode, nms 41 | from modules import AverageMeter 42 | from data import DetectionDataset, custum_collate 43 | from torchvision import transforms 44 | from data.transforms import Resize 45 | from models.retinanet_shared_heads import build_retinanet_shared_heads 46 | 47 | def str2bool(v): 48 | return v.lower() in ("yes", "true", "t", "1") 49 | 50 | def make_01(v): 51 | return 1 if v>0 else 0 52 | 53 | parser = argparse.ArgumentParser(description='Training single stage FPN with OHEM, resnet as backbone') 54 | # Name of backbone networ, e.g. resnet18, resnet34, resnet50, resnet101 resnet152 are supported 55 | parser.add_argument('--basenet', default='resnet50', help='pretrained base model') 56 | # if output heads are have shared features or not: 0 is no-shareing else sharining enabled 57 | parser.add_argument('--multi_scale', default=False, type=str2bool,help='perfrom multiscale training') 58 | parser.add_argument('--shared_heads', default=0, type=int,help='4 head layers') 59 | parser.add_argument('--num_head_layers', default=4, type=int,help='0 mean no shareding more than 0 means shareing') 60 | parser.add_argument('--use_bias', default=True, type=str2bool,help='0 mean no bias in head layears') 61 | # Name of the dataset only voc or coco are supported 62 | parser.add_argument('--dataset', default='esad', help='pretrained base model') 63 | # Input size of image only 600 is supprted at the moment 64 | parser.add_argument('--min_size', default=600, type=int, help='Input Size for FPN') 65 | parser.add_argument('--max_size', default=1080, type=int, help='Input Size for FPN') 66 | # data loading argumnets 67 | parser.add_argument('--batch_size', default=16, type=int, help='Batch size for training') 68 | # Number of worker to load data in parllel 69 | parser.add_argument('--num_workers', '-j', default=4, type=int, help='Number of workers used in dataloading') 70 | # optimiser hyperparameters 71 | parser.add_argument('--optim', default='SGD', type=str, help='Optimiser type') 72 | parser.add_argument('--resume', default=0, type=int, help='Resume from given iterations') 73 | parser.add_argument('--max_iter', default=9000, type=int, help='Number of training iterations') 74 | parser.add_argument('--lr', '--learning-rate', default=0.01, type=float, help='initial learning rate') 75 | parser.add_argument('--momentum', default=0.9, type=float, help='momentum') 76 | parser.add_argument('--loss_type', default='mbox', type=str, help='loss_type') 77 | parser.add_argument('--milestones', default='6000,8000', type=str, help='Chnage the lr @') 78 | parser.add_argument('--gammas', default='0.1,0.1', type=str, help='Gamma update for SGD') 79 | parser.add_argument('--weight_decay', default=1e-4, type=float, help='Weight decay for SGD') 80 | 81 | # Freeze layers or not 82 | parser.add_argument('--fbn','--freeze_bn', default=True, type=str2bool, help='freeze bn layers if true or else keep updating bn layers') 83 | parser.add_argument('--freezeupto', default=1, type=int, help='layer group number in ResNet up to which needs to be frozen') 84 | 85 | # Loss function matching threshold 86 | parser.add_argument('--positive_threshold', default=0.5, type=float, help='Min Jaccard index for matching') 87 | parser.add_argument('--negative_threshold', default=0.4, type=float, help='Min Jaccard index for matching') 88 | 89 | # Evaluation hyperparameters 90 | parser.add_argument('--intial_val', default=5000, type=int, help='Initial number of training iterations before evaluation') 91 | parser.add_argument('--val_step', default=1000, type=int, help='Number of training iterations before evaluation') 92 | parser.add_argument('--iou_thresh', default=0.25, type=float, help='Evaluation threshold') 93 | parser.add_argument('--conf_thresh', default=0.05, type=float, help='Confidence threshold for evaluation') 94 | parser.add_argument('--nms_thresh', default=0.45, type=float, help='NMS threshold') 95 | parser.add_argument('--topk', default=50, type=int, help='topk for evaluation') 96 | 97 | # Progress logging 98 | parser.add_argument('--log_start', default=10, type=int, help='start loging after k steps for text/tensorboard') # Let initial ripples settle down 99 | parser.add_argument('--log_step', default=1, type=int, help='Log every k steps for text/tensorboard') 100 | parser.add_argument('--tensorboard', default=True, type=str2bool, help='Use tensorboard for loss/evalaution visualization') 101 | 102 | # Program arguments 103 | parser.add_argument('--man_seed', default=123, type=int, help='manualseed for reproduction') 104 | parser.add_argument('--multi_gpu', default=True, type=str2bool, help='If more than 0 then use all visible GPUs by default only one GPU used ') 105 | 106 | # Use CUDA_VISIBLE_DEVICES=0,1,4,6 to select GPUs to use 107 | parser.add_argument('--data_root', default='/mnt/mercury-fast/datasets/', help='Location to root directory fo dataset') # /mnt/mars-fast/datasets/ 108 | parser.add_argument('--save_root', default='/mnt/mercury-alpha/', help='Location to save checkpoint models') # /mnt/sun-gamma/datasets/ 109 | # parser.add_argument('--model_dir', default='/mnt/sun-beta/vivek/weights/', help='Location to where imagenet pretrained models exists') # /mnt/mars-fast/datasets/ 110 | parser.add_argument('--model_dir', default='/mnt/mars-gamma/global-models/pytorch-imagenet/', help='Location to where imagenet pretrained models exists') # /mnt/mars-fast/datasets/ 111 | # args.model_dir = '' 112 | ## Parse arguments 113 | args = parser.parse_args() 114 | 115 | args = utils.set_args(args) # set directories and subsets fo datasets 116 | 117 | if args.tensorboard: 118 | from tensorboardX import SummaryWriter 119 | 120 | ## set random seeds and global settings 121 | np.random.seed(args.man_seed) 122 | torch.manual_seed(args.man_seed) 123 | torch.cuda.manual_seed_all(args.man_seed) 124 | torch.set_default_tensor_type('torch.FloatTensor') 125 | 126 | 127 | def main(): 128 | 129 | args.exp_name = utils.create_exp_name(args) 130 | args.save_root += args.dataset+'/' 131 | args.data_root += args.dataset+'/' 132 | args.save_root = args.save_root+'cache/'+args.exp_name+'/' 133 | 134 | if not os.path.isdir(args.save_root): #if save directory doesn't exist create it 135 | os.makedirs(args.save_root) 136 | 137 | source_dir = args.save_root+'/source/' # where to save the source 138 | utils.copy_source(source_dir) # make a copy of source files used for training as a snapshot 139 | 140 | print('\nLoading Datasets') 141 | 142 | train_transform = transforms.Compose([ 143 | Resize(args.min_size, args.max_size), 144 | transforms.ToTensor(), 145 | transforms.Normalize(mean=args.means, std=args.stds)]) 146 | 147 | train_dataset = DetectionDataset(root= args.data_root, train=True, input_sets=['train/set1','train/set2'], transform=train_transform) 148 | print('Done Loading Dataset Train Dataset :::>>>\n',train_dataset.print_str) 149 | val_transform = transforms.Compose([ 150 | Resize(args.min_size, args.max_size), 151 | transforms.ToTensor(), 152 | transforms.Normalize(mean=args.means,std=args.stds)]) 153 | 154 | val_dataset = DetectionDataset(root= args.data_root, train=False, input_sets=['val/obj'], transform=val_transform, full_test=False) 155 | print('Done Loading Dataset Validation Dataset :::>>>\n',val_dataset.print_str) 156 | 157 | args.num_classes = len(train_dataset.classes) + 1 158 | args.classes = train_dataset.classes 159 | args.use_bias = args.use_bias>0 160 | args.head_size = 256 161 | 162 | net = build_retinanet_shared_heads(args).cuda() 163 | 164 | # print(net) 165 | if args.multi_gpu: 166 | print('\nLets do dataparallel\n') 167 | net = torch.nn.DataParallel(net) 168 | 169 | if args.fbn: 170 | if args.multi_gpu: 171 | net.module.backbone_net.apply(utils.set_bn_eval) 172 | else: 173 | net.backbone_net.apply(utils.set_bn_eval) 174 | 175 | optimizer, scheduler, solver_print_str = get_optim(args, net) 176 | 177 | train(args, net, optimizer, scheduler, train_dataset, val_dataset, solver_print_str) 178 | 179 | 180 | def train(args, net, optimizer, scheduler, train_dataset, val_dataset, solver_print_str): 181 | 182 | args.start_iteration = 0 183 | if args.resume>100: 184 | args.start_iteration = args.resume 185 | args.iteration = args.start_iteration 186 | for _ in range(args.iteration-1): 187 | scheduler.step() 188 | model_file_name = '{:s}/model_{:06d}.pth'.format(args.save_root, args.start_iteration) 189 | optimizer_file_name = '{:s}/optimizer_{:06d}.pth'.format(args.save_root, args.start_iteration) 190 | net.load_state_dict(torch.load(model_file_name)) 191 | optimizer.load_state_dict(torch.load(optimizer_file_name)) 192 | 193 | # anchors = anchors.cuda(0, non_blocking=True) 194 | if args.tensorboard: 195 | log_dir = args.save_root+'tensorboard-{date:%m-%d-%Hx}.log'.format(date=datetime.datetime.now()) 196 | sw = SummaryWriter(log_dir) 197 | log_file = open(args.save_root+'training.text{date:%m-%d-%Hx}.txt'.format(date=datetime.datetime.now()), 'w', 1) 198 | log_file.write(args.exp_name+'\n') 199 | 200 | for arg in sorted(vars(args)): 201 | print(arg, getattr(args, arg)) 202 | log_file.write(str(arg)+': '+str(getattr(args, arg))+'\n') 203 | log_file.write(str(net)) 204 | log_file.write(solver_print_str) 205 | net.train() 206 | 207 | 208 | # loss counters 209 | batch_time = AverageMeter() 210 | data_time = AverageMeter() 211 | losses = AverageMeter() 212 | loc_losses = AverageMeter() 213 | cls_losses = AverageMeter() 214 | 215 | # train_dataset = DetectionDatasetDatasetDatasetDatasetDataset(args, 'train', BaseTransform(args.input_dim, args.means, args.stds)) 216 | 217 | log_file.write(train_dataset.print_str) 218 | log_file.write(val_dataset.print_str) 219 | print('Train-DATA :::>>>', train_dataset.print_str) 220 | print('VAL-DATA :::>>>', val_dataset.print_str) 221 | epoch_size = len(train_dataset) // args.batch_size 222 | # print('Training FPN on ', train_dataset.dataset,'\n') 223 | 224 | 225 | train_data_loader = data_utils.DataLoader(train_dataset, args.batch_size, num_workers=args.num_workers, 226 | shuffle=True, pin_memory=True, collate_fn=custum_collate, drop_last=True) 227 | 228 | 229 | val_data_loader = data_utils.DataLoader(val_dataset, args.batch_size, num_workers=args.num_workers, 230 | shuffle=False, pin_memory=True, collate_fn=custum_collate) 231 | 232 | torch.cuda.synchronize() 233 | start = time.perf_counter() 234 | iteration = args.start_iteration 235 | eopch = 0 236 | num_bpe = len(train_data_loader) 237 | while iteration <= args.max_iter: 238 | for i, (images, gts, counts, _, _) in enumerate(train_data_loader): 239 | if iteration > args.max_iter: 240 | break 241 | iteration += 1 242 | 243 | # pdb.set_trace() 244 | epoch = int(iteration/num_bpe) 245 | images = images.cuda(0, non_blocking=True) 246 | gts = gts.cuda(0, non_blocking=True) 247 | counts = counts.cuda(0, non_blocking=True) 248 | # forward 249 | torch.cuda.synchronize() 250 | data_time.update(time.perf_counter() - start) 251 | 252 | # print(images.size(), anchors.size()) 253 | optimizer.zero_grad() 254 | # pdb.set_trace() 255 | # print(gts.shape, counts.shape, images.shape) 256 | loss_l, loss_c = net(images, gts, counts) 257 | loss_l, loss_c = loss_l.mean() , loss_c.mean() 258 | loss = loss_l + loss_c 259 | 260 | loss.backward() 261 | optimizer.step() 262 | scheduler.step() 263 | 264 | # pdb.set_trace() 265 | loc_loss = loss_l.item() 266 | conf_loss = loss_c.item() 267 | 268 | if loc_loss>300: 269 | lline = '\n\n\n We got faulty LOCATION loss {} {} \n\n\n'.format(loc_loss, conf_loss) 270 | log_file.write(lline) 271 | print(lline) 272 | loc_loss = 20.0 273 | if conf_loss>300: 274 | lline = '\n\n\n We got faulty CLASSIFICATION loss {} {} \n\n\n'.format(loc_loss, conf_loss) 275 | log_file.write(lline) 276 | print(lline) 277 | conf_loss = 20.0 278 | 279 | # print('Loss data type ',type(loc_loss)) 280 | loc_losses.update(loc_loss) 281 | cls_losses.update(conf_loss) 282 | losses.update((loc_loss + conf_loss)/2.0) 283 | 284 | torch.cuda.synchronize() 285 | batch_time.update(time.perf_counter() - start) 286 | start = time.perf_counter() 287 | 288 | if iteration % args.log_step == 0 and iteration > args.log_start: 289 | if args.tensorboard: 290 | sw.add_scalars('Classification', {'val': cls_losses.val, 'avg':cls_losses.avg},iteration) 291 | sw.add_scalars('Localisation', {'val': loc_losses.val, 'avg':loc_losses.avg},iteration) 292 | sw.add_scalars('Overall', {'val': losses.val, 'avg':losses.avg},iteration) 293 | 294 | print_line = 'Itration [{:d}]{:06d}/{:06d} loc-loss {:.2f}({:.2f}) cls-loss {:.2f}({:.2f}) ' \ 295 | 'average-loss {:.2f}({:.2f}) DataTime{:0.2f}({:0.2f}) Timer {:0.2f}({:0.2f})'.format( epoch, 296 | iteration, args.max_iter, loc_losses.val, loc_losses.avg, cls_losses.val, 297 | cls_losses.avg, losses.val, losses.avg, 10*data_time.val, 10*data_time.avg, 10*batch_time.val, 10*batch_time.avg) 298 | 299 | log_file.write(print_line+'\n') 300 | print(print_line) 301 | if iteration % (args.log_step*10) == 0: 302 | print_line = args.exp_name 303 | log_file.write(print_line+'\n') 304 | print(print_line) 305 | 306 | 307 | if (iteration % args.val_step == 0 or iteration== args.intial_val or iteration == args.max_iter) and iteration>0: 308 | torch.cuda.synchronize() 309 | tvs = time.perf_counter() 310 | print('Saving state, iter:', iteration) 311 | torch.save(net.state_dict(), '{:s}/model_{:06d}.pth'.format(args.save_root, iteration)) 312 | torch.save(optimizer.state_dict(), '{:s}/optimizer_{:06d}.pth'.format(args.save_root, iteration)) 313 | net.eval() # switch net to evaluation mode 314 | mAP, ap_all, ap_strs, _ = validate(args, net, val_data_loader, val_dataset, iteration, iou_thresh=args.iou_thresh) 315 | net.train() 316 | if args.fbn: 317 | if args.multi_gpu: 318 | net.module.backbone_net.apply(utils.set_bn_eval) 319 | else: 320 | net.backbone_net.apply(utils.set_bn_eval) 321 | 322 | for ap_str in ap_strs: 323 | print(ap_str) 324 | log_file.write(ap_str+'\n') 325 | ptr_str = '\nMEANAP:::=>'+str(mAP)+'\n' 326 | print(ptr_str) 327 | log_file.write(ptr_str) 328 | 329 | if args.tensorboard: 330 | sw.add_scalar('mAP@0.5', mAP, iteration) 331 | class_AP_group = dict() 332 | for c, ap in enumerate(ap_all): 333 | class_AP_group[args.classes[c]] = ap 334 | sw.add_scalars('ClassAPs', class_AP_group, iteration) 335 | 336 | torch.cuda.synchronize() 337 | t0 = time.perf_counter() 338 | prt_str = '\nValidation TIME::: {:0.3f}\n\n'.format(t0-tvs) 339 | print(prt_str) 340 | log_file.write(ptr_str) 341 | 342 | log_file.close() 343 | 344 | 345 | def validate(args, net, val_data_loader, val_dataset, iteration_num, iou_thresh=0.5): 346 | """Test a FPN network on an image database.""" 347 | print('Validating at ', iteration_num) 348 | num_images = len(val_dataset) 349 | num_classes = args.num_classes 350 | 351 | det_boxes = [[] for _ in range(num_classes-1)] 352 | gt_boxes = [] 353 | print_time = True 354 | val_step = 20 355 | count = 0 356 | torch.cuda.synchronize() 357 | ts = time.perf_counter() 358 | activation = nn.Sigmoid().cuda() 359 | if args.loss_type == 'mbox': 360 | activation = nn.Softmax(dim=2).cuda() 361 | 362 | dict_for_json_dump = {} 363 | 364 | with torch.no_grad(): 365 | for val_itr, (images, targets, batch_counts, img_indexs, wh) in enumerate(val_data_loader): 366 | 367 | torch.cuda.synchronize() 368 | t1 = time.perf_counter() 369 | 370 | batch_size = images.size(0) 371 | 372 | images = images.cuda(0, non_blocking=True) 373 | decoded_boxes, conf_data = net(images) 374 | 375 | conf_scores_all = activation(conf_data).clone() 376 | 377 | if print_time and val_itr%val_step == 0: 378 | torch.cuda.synchronize() 379 | tf = time.perf_counter() 380 | print('Forward Time {:0.3f}'.format(tf-t1)) 381 | 382 | for b in range(batch_size): 383 | width, height = wh[b][0], wh[b][1] 384 | gt = targets[b, :batch_counts[b]].numpy() 385 | gt_boxes.append(gt) 386 | # decoded_boxes = decode(loc_data[b], anchors).clone() 387 | conf_scores = conf_scores_all[b] 388 | #Apply nms per class and obtain the results 389 | decoded_boxes_b = decoded_boxes[b] 390 | for cl_ind in range(1, num_classes): 391 | # pdb.set_trace() 392 | scores = conf_scores[:, cl_ind].squeeze() 393 | if args.loss_type == 'yolo': 394 | scores = conf_scores[:, cl_ind].squeeze() * conf_scores[:, 0].squeeze() * 5.0 395 | c_mask = scores.gt(args.conf_thresh) # greater than minmum threshold 396 | scores = scores[c_mask].squeeze() 397 | # print('scores size',c_mask.sum()) 398 | if scores.dim() == 0: 399 | # print(len(''), ' dim ==0 ') 400 | det_boxes[cl_ind - 1].append(np.asarray([])) 401 | continue 402 | # boxes = decoded_boxes_b.clone() 403 | l_mask = c_mask.unsqueeze(1).expand_as(decoded_boxes_b) 404 | boxes = decoded_boxes_b[l_mask].clone().view(-1, 4) 405 | # idx of highest scoring and non-overlapping boxes per class 406 | ids, counts = nms(boxes, scores, args.nms_thresh, args.topk*20) # idsn - ids after nms 407 | scores = scores[ids[:min(args.topk,counts)]].cpu().numpy() 408 | # pick = min(scores.shape[0], 20) 409 | # scores = scores[:pick] 410 | boxes = boxes[ids[:min(args.topk,counts)]].cpu().numpy() 411 | 412 | for ik in range(boxes.shape[0]): 413 | boxes[ik, 0] = max(0, boxes[ik, 0]) 414 | boxes[ik, 2] = min(width, boxes[ik, 2]) 415 | boxes[ik, 1] = max(0, boxes[ik, 1]) 416 | boxes[ik, 3] = min(height, boxes[ik, 3]) 417 | 418 | cls_dets = np.hstack((boxes, scores[:, np.newaxis])).astype(np.float32, copy=True) 419 | det_boxes[cl_ind-1].append(cls_dets) 420 | count += 1 421 | 422 | if print_time and val_itr%val_step == 0: 423 | torch.cuda.synchronize() 424 | te = time.perf_counter() 425 | print('im_detect: {:d}/{:d} time taken {:0.3f}'.format(count, num_images, te-ts)) 426 | torch.cuda.synchronize() 427 | ts = time.perf_counter() 428 | if print_time and val_itr%val_step == 0: 429 | torch.cuda.synchronize() 430 | te = time.perf_counter() 431 | print('NMS stuff Time {:0.3f}'.format(te - tf)) 432 | 433 | print('Evaluating detections for itration number ', iteration_num) 434 | return evaluate_detections(gt_boxes, det_boxes, val_dataset.classes, iou_thresh=iou_thresh) 435 | 436 | if __name__ == '__main__': 437 | main() 438 | -------------------------------------------------------------------------------- /samples/testset.log: -------------------------------------------------------------------------------- 1 | FPN800x1440-0-esad-resnet50-hl4s0-bn1f1b1-bs16-SGD-lr010000-mbox 2 | /mnt/mercury-alpha/esad/cache/FPN800x1440-0-esad-resnet50-hl4s0-bn1f1b1-bs16-SGD-lr010000-mbox/model_006000.pth 3 | Testing net 4 | 5 | 6 | IOU Threshold: 0.05:: 7 | 8 | CuttingMesocolon : 188.0 : 24 : 0.076505940867643 9 | PullingVasDeferens : 113.0 : 247 : 0.13968878906146953 10 | ClippingVasDeferens : 48.0 : 21 : 0.034722222222222224 11 | CuttingVasDeferens : 36.0 : 7 : 0.0 12 | ClippingTissue : 15.0 : 155 : 0.3449687198313423 13 | PullingSeminalVesicle : 436.0 : 885 : 0.07906419371826832 14 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 15 | CuttingSeminalVesicle : 307.0 : 562 : 0.08578779294895178 16 | SuckingBlood : 1696.0 : 4650 : 0.592377913475368 17 | SuckingSmoke : 771.0 : 1299 : 0.14836011987986597 18 | PullingTissue : 2024.0 : 11235 : 0.3818600204713837 19 | CuttingTissue : 2055.0 : 4763 : 0.44615114322809635 20 | BaggingProstate : 37.0 : 19 : 0.0 21 | BladderNeckDissection : 519.0 : 673 : 0.037317486772579546 22 | BladderAnastomosis : 1828.0 : 10618 : 0.6762890453043682 23 | PullingProstate : 451.0 : 2670 : 0.09745307513177406 24 | ClippingBladderNeck : 18.0 : 157 : 0.04087301587301588 25 | CuttingThread : 40.0 : 323 : 0.1208021978021978 26 | UrethraDissection : 439.0 : 1007 : 0.12396219041441664 27 | CuttingProstate : 48.0 : 1988 : 0.017112674043371124 28 | PullingBladderNeck : 105.0 : 2473 : 5.1666236114699044e-05 29 | 30 | MEANAP:::=>16.3969 31 | 32 | 33 | 34 | IOU Threshold: 0.10:: 35 | 36 | CuttingMesocolon : 188.0 : 24 : 0.076505940867643 37 | PullingVasDeferens : 113.0 : 247 : 0.13968878906146953 38 | ClippingVasDeferens : 48.0 : 21 : 0.034722222222222224 39 | CuttingVasDeferens : 36.0 : 7 : 0.0 40 | ClippingTissue : 15.0 : 155 : 0.3449687198313423 41 | PullingSeminalVesicle : 436.0 : 885 : 0.0761964955730446 42 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 43 | CuttingSeminalVesicle : 307.0 : 562 : 0.08578779294895178 44 | SuckingBlood : 1696.0 : 4650 : 0.591367379744596 45 | SuckingSmoke : 771.0 : 1299 : 0.14465522919185106 46 | PullingTissue : 2024.0 : 11235 : 0.3791390030347995 47 | CuttingTissue : 2055.0 : 4763 : 0.44451098992843935 48 | BaggingProstate : 37.0 : 19 : 0.0 49 | BladderNeckDissection : 519.0 : 673 : 0.03680035421476572 50 | BladderAnastomosis : 1828.0 : 10618 : 0.6634599882054855 51 | PullingProstate : 451.0 : 2670 : 0.09415575402386711 52 | ClippingBladderNeck : 18.0 : 157 : 0.04087301587301588 53 | CuttingThread : 40.0 : 323 : 0.1208021978021978 54 | UrethraDissection : 439.0 : 1007 : 0.12064077162651374 55 | CuttingProstate : 48.0 : 1988 : 0.017112674043371124 56 | PullingBladderNeck : 105.0 : 2473 : 5.1666236114699044e-05 57 | 58 | MEANAP:::=>16.2449 59 | 60 | 61 | 62 | IOU Threshold: 0.15:: 63 | 64 | CuttingMesocolon : 188.0 : 24 : 0.076505940867643 65 | PullingVasDeferens : 113.0 : 247 : 0.1376127224299384 66 | ClippingVasDeferens : 48.0 : 21 : 0.034722222222222224 67 | CuttingVasDeferens : 36.0 : 7 : 0.0 68 | ClippingTissue : 15.0 : 155 : 0.3449687198313423 69 | PullingSeminalVesicle : 436.0 : 885 : 0.07585142813913702 70 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 71 | CuttingSeminalVesicle : 307.0 : 562 : 0.08578779294895178 72 | SuckingBlood : 1696.0 : 4650 : 0.5854552338422438 73 | SuckingSmoke : 771.0 : 1299 : 0.14388807928124944 74 | PullingTissue : 2024.0 : 11235 : 0.3761817470154978 75 | CuttingTissue : 2055.0 : 4763 : 0.4421176441804723 76 | BaggingProstate : 37.0 : 19 : 0.0 77 | BladderNeckDissection : 519.0 : 673 : 0.03680035421476572 78 | BladderAnastomosis : 1828.0 : 10618 : 0.5677129094214076 79 | PullingProstate : 451.0 : 2670 : 0.09009914607160371 80 | ClippingBladderNeck : 18.0 : 157 : 0.04087301587301588 81 | CuttingThread : 40.0 : 323 : 0.1208021978021978 82 | UrethraDissection : 439.0 : 1007 : 0.12064077162651374 83 | CuttingProstate : 48.0 : 1988 : 0.017112674043371124 84 | PullingBladderNeck : 105.0 : 2473 : 5.1666236114699044e-05 85 | 86 | MEANAP:::=>15.7009 87 | 88 | 89 | 90 | IOU Threshold: 0.20:: 91 | 92 | CuttingMesocolon : 188.0 : 24 : 0.076505940867643 93 | PullingVasDeferens : 113.0 : 247 : 0.13568068545286813 94 | ClippingVasDeferens : 48.0 : 21 : 0.034722222222222224 95 | CuttingVasDeferens : 36.0 : 7 : 0.0 96 | ClippingTissue : 15.0 : 155 : 0.3449687198313423 97 | PullingSeminalVesicle : 436.0 : 885 : 0.07498295558615475 98 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 99 | CuttingSeminalVesicle : 307.0 : 562 : 0.08272217951192734 100 | SuckingBlood : 1696.0 : 4650 : 0.5731528763538453 101 | SuckingSmoke : 771.0 : 1299 : 0.14152160968780667 102 | PullingTissue : 2024.0 : 11235 : 0.3720404308456675 103 | CuttingTissue : 2055.0 : 4763 : 0.4344474199197051 104 | BaggingProstate : 37.0 : 19 : 0.0 105 | BladderNeckDissection : 519.0 : 673 : 0.03583860609943819 106 | BladderAnastomosis : 1828.0 : 10618 : 0.48192249786432817 107 | PullingProstate : 451.0 : 2670 : 0.08403597395063198 108 | ClippingBladderNeck : 18.0 : 157 : 0.04087301587301588 109 | CuttingThread : 40.0 : 323 : 0.11805952380952381 110 | UrethraDissection : 439.0 : 1007 : 0.11686169990408221 111 | CuttingProstate : 48.0 : 1988 : 0.017112674043371124 112 | PullingBladderNeck : 105.0 : 2473 : 5.1666236114699044e-05 113 | 114 | MEANAP:::=>15.0738 115 | 116 | 117 | 118 | IOU Threshold: 0.25:: 119 | 120 | CuttingMesocolon : 188.0 : 24 : 0.076505940867643 121 | PullingVasDeferens : 113.0 : 247 : 0.1258413566733133 122 | ClippingVasDeferens : 48.0 : 21 : 0.034722222222222224 123 | CuttingVasDeferens : 36.0 : 7 : 0.0 124 | ClippingTissue : 15.0 : 155 : 0.3449687198313423 125 | PullingSeminalVesicle : 436.0 : 885 : 0.07498295558615475 126 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 127 | CuttingSeminalVesicle : 307.0 : 562 : 0.08166859252688634 128 | SuckingBlood : 1696.0 : 4650 : 0.5523332048300194 129 | SuckingSmoke : 771.0 : 1299 : 0.139378380934163 130 | PullingTissue : 2024.0 : 11235 : 0.36139232389086295 131 | CuttingTissue : 2055.0 : 4763 : 0.42037927991047175 132 | BaggingProstate : 37.0 : 19 : 0.0 133 | BladderNeckDissection : 519.0 : 673 : 0.034763875188418725 134 | BladderAnastomosis : 1828.0 : 10618 : 0.39966086302852954 135 | PullingProstate : 451.0 : 2670 : 0.07578916704309624 136 | ClippingBladderNeck : 18.0 : 157 : 0.04087301587301588 137 | CuttingThread : 40.0 : 323 : 0.03480967458684202 138 | UrethraDissection : 439.0 : 1007 : 0.10637036755310914 139 | CuttingProstate : 48.0 : 1988 : 0.015627223765365468 140 | PullingBladderNeck : 105.0 : 2473 : 2.296277160653291e-05 141 | 142 | MEANAP:::=>13.9052 143 | 144 | 145 | 146 | IOU Threshold: 0.30:: 147 | 148 | CuttingMesocolon : 188.0 : 24 : 0.076505940867643 149 | PullingVasDeferens : 113.0 : 247 : 0.11925171385285943 150 | ClippingVasDeferens : 48.0 : 21 : 0.034722222222222224 151 | CuttingVasDeferens : 36.0 : 7 : 0.0 152 | ClippingTissue : 15.0 : 155 : 0.3449687198313423 153 | PullingSeminalVesicle : 436.0 : 885 : 0.07252887946569196 154 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 155 | CuttingSeminalVesicle : 307.0 : 562 : 0.06786709326006056 156 | SuckingBlood : 1696.0 : 4650 : 0.5208964929830335 157 | SuckingSmoke : 771.0 : 1299 : 0.1338982217890501 158 | PullingTissue : 2024.0 : 11235 : 0.33955369272102465 159 | CuttingTissue : 2055.0 : 4763 : 0.39203289855031076 160 | BaggingProstate : 37.0 : 19 : 0.0 161 | BladderNeckDissection : 519.0 : 673 : 0.031953441194545 162 | BladderAnastomosis : 1828.0 : 10618 : 0.33520817243426493 163 | PullingProstate : 451.0 : 2670 : 0.06999573134750064 164 | ClippingBladderNeck : 18.0 : 157 : 0.04087301587301588 165 | CuttingThread : 40.0 : 323 : 0.030414539198830182 166 | UrethraDissection : 439.0 : 1007 : 0.08861691432924844 167 | CuttingProstate : 48.0 : 1988 : 0.013714985440083661 168 | PullingBladderNeck : 105.0 : 2473 : 2.296277160653291e-05 169 | 170 | MEANAP:::=>12.9192 171 | 172 | 173 | 174 | IOU Threshold: 0.35:: 175 | 176 | CuttingMesocolon : 188.0 : 24 : 0.07161142987175528 177 | PullingVasDeferens : 113.0 : 247 : 0.1103547729691511 178 | ClippingVasDeferens : 48.0 : 21 : 0.034722222222222224 179 | CuttingVasDeferens : 36.0 : 7 : 0.0 180 | ClippingTissue : 15.0 : 155 : 0.32959965911574696 181 | PullingSeminalVesicle : 436.0 : 885 : 0.0702430267510466 182 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 183 | CuttingSeminalVesicle : 307.0 : 562 : 0.06786709326006056 184 | SuckingBlood : 1696.0 : 4650 : 0.4556216698544028 185 | SuckingSmoke : 771.0 : 1299 : 0.12207906689571593 186 | PullingTissue : 2024.0 : 11235 : 0.2994224565636735 187 | CuttingTissue : 2055.0 : 4763 : 0.34843722739783134 188 | BaggingProstate : 37.0 : 19 : 0.0 189 | BladderNeckDissection : 519.0 : 673 : 0.031320376034508324 190 | BladderAnastomosis : 1828.0 : 10618 : 0.25499950541731164 191 | PullingProstate : 451.0 : 2670 : 0.05894904071361382 192 | ClippingBladderNeck : 18.0 : 157 : 0.04087301587301588 193 | CuttingThread : 40.0 : 323 : 0.008684412586317507 194 | UrethraDissection : 439.0 : 1007 : 0.08167784824177075 195 | CuttingProstate : 48.0 : 1988 : 0.013299828435252743 196 | PullingBladderNeck : 105.0 : 2473 : 2.296277160653291e-05 197 | 198 | MEANAP:::=>11.4276 199 | 200 | 201 | 202 | IOU Threshold: 0.40:: 203 | 204 | CuttingMesocolon : 188.0 : 24 : 0.07161142987175528 205 | PullingVasDeferens : 113.0 : 247 : 0.10601671176022477 206 | ClippingVasDeferens : 48.0 : 21 : 0.034722222222222224 207 | CuttingVasDeferens : 36.0 : 7 : 0.0 208 | ClippingTissue : 15.0 : 155 : 0.32959965911574696 209 | PullingSeminalVesicle : 436.0 : 885 : 0.06639559957622737 210 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 211 | CuttingSeminalVesicle : 307.0 : 562 : 0.05998073024878353 212 | SuckingBlood : 1696.0 : 4650 : 0.39625160321733743 213 | SuckingSmoke : 771.0 : 1299 : 0.10592600885337702 214 | PullingTissue : 2024.0 : 11235 : 0.23234509470368292 215 | CuttingTissue : 2055.0 : 4763 : 0.263974641128042 216 | BaggingProstate : 37.0 : 19 : 0.0 217 | BladderNeckDissection : 519.0 : 673 : 0.025827961742254955 218 | BladderAnastomosis : 1828.0 : 10618 : 0.16983198316547893 219 | PullingProstate : 451.0 : 2670 : 0.05035292216542619 220 | ClippingBladderNeck : 18.0 : 157 : 0.025396825396825393 221 | CuttingThread : 40.0 : 323 : 0.008684412586317507 222 | UrethraDissection : 439.0 : 1007 : 0.07123317042315509 223 | CuttingProstate : 48.0 : 1988 : 0.010725570163445254 224 | PullingBladderNeck : 105.0 : 2473 : 2.296277160653291e-05 225 | 226 | MEANAP:::=>9.6614 227 | 228 | 229 | 230 | IOU Threshold: 0.45:: 231 | 232 | CuttingMesocolon : 188.0 : 24 : 0.058500310182099925 233 | PullingVasDeferens : 113.0 : 247 : 0.10601671176022477 234 | ClippingVasDeferens : 48.0 : 21 : 0.006944444444444444 235 | CuttingVasDeferens : 36.0 : 7 : 0.0 236 | ClippingTissue : 15.0 : 155 : 0.21196718752911214 237 | PullingSeminalVesicle : 436.0 : 885 : 0.05547101602172138 238 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 239 | CuttingSeminalVesicle : 307.0 : 562 : 0.04582401558123676 240 | SuckingBlood : 1696.0 : 4650 : 0.3203143537177314 241 | SuckingSmoke : 771.0 : 1299 : 0.08305124790090193 242 | PullingTissue : 2024.0 : 11235 : 0.1740095553971983 243 | CuttingTissue : 2055.0 : 4763 : 0.19683956806111932 244 | BaggingProstate : 37.0 : 19 : 0.0 245 | BladderNeckDissection : 519.0 : 673 : 0.020548113314442864 246 | BladderAnastomosis : 1828.0 : 10618 : 0.10265484349645226 247 | PullingProstate : 451.0 : 2670 : 0.040591046011207096 248 | ClippingBladderNeck : 18.0 : 157 : 0.015701142452734806 249 | CuttingThread : 40.0 : 323 : 0.0012500000000000002 250 | UrethraDissection : 439.0 : 1007 : 0.041393046422289324 251 | CuttingProstate : 48.0 : 1988 : 0.007534722222222222 252 | PullingBladderNeck : 105.0 : 2473 : 2.296277160653291e-05 253 | 254 | MEANAP:::=>7.0887 255 | 256 | 257 | 258 | IOU Threshold: 0.50:: 259 | 260 | CuttingMesocolon : 188.0 : 24 : 0.050357183492352436 261 | PullingVasDeferens : 113.0 : 247 : 0.07713542756513739 262 | ClippingVasDeferens : 48.0 : 21 : 0.006944444444444444 263 | CuttingVasDeferens : 36.0 : 7 : 0.0 264 | ClippingTissue : 15.0 : 155 : 0.1864405085866273 265 | PullingSeminalVesicle : 436.0 : 885 : 0.04276529761783619 266 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 267 | CuttingSeminalVesicle : 307.0 : 562 : 0.03005476682632364 268 | SuckingBlood : 1696.0 : 4650 : 0.23687366867625273 269 | SuckingSmoke : 771.0 : 1299 : 0.06351370407335032 270 | PullingTissue : 2024.0 : 11235 : 0.11547076895266387 271 | CuttingTissue : 2055.0 : 4763 : 0.13399486884033662 272 | BaggingProstate : 37.0 : 19 : 0.0 273 | BladderNeckDissection : 519.0 : 673 : 0.011781624326938125 274 | BladderAnastomosis : 1828.0 : 10618 : 0.05726045935500155 275 | PullingProstate : 451.0 : 2670 : 0.02558475361972436 276 | ClippingBladderNeck : 18.0 : 157 : 0.015701142452734806 277 | CuttingThread : 40.0 : 323 : 0.0012500000000000002 278 | UrethraDissection : 439.0 : 1007 : 0.02455680921627028 279 | CuttingProstate : 48.0 : 1988 : 0.0061925724637681145 280 | PullingBladderNeck : 105.0 : 2473 : 2.296277160653291e-05 281 | 282 | MEANAP:::=>5.1710 283 | 284 | 285 | 286 | IOU Threshold: 0.55:: 287 | 288 | CuttingMesocolon : 188.0 : 24 : 0.019462910889694367 289 | PullingVasDeferens : 113.0 : 247 : 0.07547171692544696 290 | ClippingVasDeferens : 48.0 : 21 : 0.006944444444444444 291 | CuttingVasDeferens : 36.0 : 7 : 0.0 292 | ClippingTissue : 15.0 : 155 : 0.1460688643337045 293 | PullingSeminalVesicle : 436.0 : 885 : 0.031805715332642634 294 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 295 | CuttingSeminalVesicle : 307.0 : 562 : 0.025611733641161455 296 | SuckingBlood : 1696.0 : 4650 : 0.1426839818592185 297 | SuckingSmoke : 771.0 : 1299 : 0.037471188128321106 298 | PullingTissue : 2024.0 : 11235 : 0.07343431918332537 299 | CuttingTissue : 2055.0 : 4763 : 0.07187060932027672 300 | BaggingProstate : 37.0 : 19 : 0.0 301 | BladderNeckDissection : 519.0 : 673 : 0.004394583859862796 302 | BladderAnastomosis : 1828.0 : 10618 : 0.035389841959986734 303 | PullingProstate : 451.0 : 2670 : 0.012307695755425839 304 | ClippingBladderNeck : 18.0 : 157 : 0.00035385704175513094 305 | CuttingThread : 40.0 : 323 : 0.0012500000000000002 306 | UrethraDissection : 439.0 : 1007 : 0.011852773223004341 307 | CuttingProstate : 48.0 : 1988 : 0.0034573317063249947 308 | PullingBladderNeck : 105.0 : 2473 : 2.296277160653291e-05 309 | 310 | MEANAP:::=>3.3326 311 | 312 | 313 | 314 | IOU Threshold: 0.60:: 315 | 316 | CuttingMesocolon : 188.0 : 24 : 0.007175094409136962 317 | PullingVasDeferens : 113.0 : 247 : 0.039727722486356865 318 | ClippingVasDeferens : 48.0 : 21 : 0.006944444444444444 319 | CuttingVasDeferens : 36.0 : 7 : 0.0 320 | ClippingTissue : 15.0 : 155 : 0.08082341132569443 321 | PullingSeminalVesicle : 436.0 : 885 : 0.024321509463798775 322 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 323 | CuttingSeminalVesicle : 307.0 : 562 : 0.01728854740547646 324 | SuckingBlood : 1696.0 : 4650 : 0.07728063269020664 325 | SuckingSmoke : 771.0 : 1299 : 0.02586011646361493 326 | PullingTissue : 2024.0 : 11235 : 0.03581869023708019 327 | CuttingTissue : 2055.0 : 4763 : 0.03467158457292877 328 | BaggingProstate : 37.0 : 19 : 0.0 329 | BladderNeckDissection : 519.0 : 673 : 0.0026164264845682642 330 | BladderAnastomosis : 1828.0 : 10618 : 0.02004423940019604 331 | PullingProstate : 451.0 : 2670 : 0.006512408278302797 332 | ClippingBladderNeck : 18.0 : 157 : 0.00035385704175513094 333 | CuttingThread : 40.0 : 323 : 0.0012500000000000002 334 | UrethraDissection : 439.0 : 1007 : 0.004350689691788407 335 | CuttingProstate : 48.0 : 1988 : 0.0025652430343705507 336 | PullingBladderNeck : 105.0 : 2473 : 2.296277160653291e-05 337 | 338 | MEANAP:::=>1.8458 339 | 340 | 341 | 342 | IOU Threshold: 0.65:: 343 | 344 | CuttingMesocolon : 188.0 : 24 : 0.0055033618863406086 345 | PullingVasDeferens : 113.0 : 247 : 0.023315180394826412 346 | ClippingVasDeferens : 48.0 : 21 : 0.006944444444444444 347 | CuttingVasDeferens : 36.0 : 7 : 0.0 348 | ClippingTissue : 15.0 : 155 : 0.03828282828282828 349 | PullingSeminalVesicle : 436.0 : 885 : 0.00939459835229803 350 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 351 | CuttingSeminalVesicle : 307.0 : 562 : 0.008883682228304587 352 | SuckingBlood : 1696.0 : 4650 : 0.03150362090928026 353 | SuckingSmoke : 771.0 : 1299 : 0.012937325771821105 354 | PullingTissue : 2024.0 : 11235 : 0.013256556653994517 355 | CuttingTissue : 2055.0 : 4763 : 0.01493134365180088 356 | BaggingProstate : 37.0 : 19 : 0.0 357 | BladderNeckDissection : 519.0 : 673 : 0.001267439345241978 358 | BladderAnastomosis : 1828.0 : 10618 : 0.009043958346206353 359 | PullingProstate : 451.0 : 2670 : 0.002253345222328545 360 | ClippingBladderNeck : 18.0 : 157 : 0.0 361 | CuttingThread : 40.0 : 323 : 0.0 362 | UrethraDissection : 439.0 : 1007 : 0.0014721415989170092 363 | CuttingProstate : 48.0 : 1988 : 0.0018064876957494408 364 | PullingBladderNeck : 105.0 : 2473 : 2.296277160653291e-05 365 | 366 | MEANAP:::=>0.8610 367 | 368 | 369 | 370 | IOU Threshold: 0.70:: 371 | 372 | CuttingMesocolon : 188.0 : 24 : 0.003799392097264437 373 | PullingVasDeferens : 113.0 : 247 : 0.020894788593903636 374 | ClippingVasDeferens : 48.0 : 21 : 0.006944444444444444 375 | CuttingVasDeferens : 36.0 : 7 : 0.0 376 | ClippingTissue : 15.0 : 155 : 0.03828282828282828 377 | PullingSeminalVesicle : 436.0 : 885 : 0.0036185315675232523 378 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 379 | CuttingSeminalVesicle : 307.0 : 562 : 0.007028076671536384 380 | SuckingBlood : 1696.0 : 4650 : 0.01009452409418875 381 | SuckingSmoke : 771.0 : 1299 : 0.004675846801865942 382 | PullingTissue : 2024.0 : 11235 : 0.004931315623530107 383 | CuttingTissue : 2055.0 : 4763 : 0.0046722801525806625 384 | BaggingProstate : 37.0 : 19 : 0.0 385 | BladderNeckDissection : 519.0 : 673 : 0.00023724912133268503 386 | BladderAnastomosis : 1828.0 : 10618 : 0.0030485745581155556 387 | PullingProstate : 451.0 : 2670 : 0.0012605201362144275 388 | ClippingBladderNeck : 18.0 : 157 : 0.0 389 | CuttingThread : 40.0 : 323 : 0.0 390 | UrethraDissection : 439.0 : 1007 : 0.0011967238831008605 391 | CuttingProstate : 48.0 : 1988 : 0.0014771100264388856 392 | PullingBladderNeck : 105.0 : 2473 : 0.0 393 | 394 | MEANAP:::=>0.5341 395 | 396 | 397 | 398 | IOU Threshold: 0.75:: 399 | 400 | CuttingMesocolon : 188.0 : 24 : 0.001266464032421479 401 | PullingVasDeferens : 113.0 : 247 : 0.009882005899705015 402 | ClippingVasDeferens : 48.0 : 21 : 0.006944444444444444 403 | CuttingVasDeferens : 36.0 : 7 : 0.0 404 | ClippingTissue : 15.0 : 155 : 0.01787878787878788 405 | PullingSeminalVesicle : 436.0 : 885 : 0.001389244877090977 406 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 407 | CuttingSeminalVesicle : 307.0 : 562 : 0.0016696917658226946 408 | SuckingBlood : 1696.0 : 4650 : 0.003604650866858762 409 | SuckingSmoke : 771.0 : 1299 : 0.0011076436427770826 410 | PullingTissue : 2024.0 : 11235 : 0.0015585391834554594 411 | CuttingTissue : 2055.0 : 4763 : 0.0007649653117812312 412 | BaggingProstate : 37.0 : 19 : 0.0 413 | BladderNeckDissection : 519.0 : 673 : 7.629339747962518e-05 414 | BladderAnastomosis : 1828.0 : 10618 : 0.0007534247731428676 415 | PullingProstate : 451.0 : 2670 : 0.0009936769550087652 416 | ClippingBladderNeck : 18.0 : 157 : 0.0 417 | CuttingThread : 40.0 : 323 : 0.0 418 | UrethraDissection : 439.0 : 1007 : 0.00012061987476857254 419 | CuttingProstate : 48.0 : 1988 : 0.000492932682529998 420 | PullingBladderNeck : 105.0 : 2473 : 0.0 421 | 422 | MEANAP:::=>0.2310 423 | 424 | 425 | 426 | IOU Threshold: 0.80:: 427 | 428 | CuttingMesocolon : 188.0 : 24 : 0.0007598784194528874 429 | PullingVasDeferens : 113.0 : 247 : 0.004424778761061947 430 | ClippingVasDeferens : 48.0 : 21 : 0.0 431 | CuttingVasDeferens : 36.0 : 7 : 0.0 432 | ClippingTissue : 15.0 : 155 : 0.0015151515151515152 433 | PullingSeminalVesicle : 436.0 : 885 : 0.00018100475462104278 434 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 435 | CuttingSeminalVesicle : 307.0 : 562 : 1.4477017734346725e-05 436 | SuckingBlood : 1696.0 : 4650 : 0.0009765178500719047 437 | SuckingSmoke : 771.0 : 1299 : 0.00022478613809244938 438 | PullingTissue : 2024.0 : 11235 : 0.0002673691593671409 439 | CuttingTissue : 2055.0 : 4763 : 0.00016051607250351787 440 | BaggingProstate : 37.0 : 19 : 0.0 441 | BladderNeckDissection : 519.0 : 673 : 0.0 442 | BladderAnastomosis : 1828.0 : 10618 : 0.0001722836732349737 443 | PullingProstate : 451.0 : 2670 : 0.00043164023617834143 444 | ClippingBladderNeck : 18.0 : 157 : 0.0 445 | CuttingThread : 40.0 : 323 : 0.0 446 | UrethraDissection : 439.0 : 1007 : 1.0748199750787098e-05 447 | CuttingProstate : 48.0 : 1988 : 5.6377924059446236e-05 448 | PullingBladderNeck : 105.0 : 2473 : 0.0 449 | 450 | MEANAP:::=>0.0438 451 | 452 | 453 | 454 | IOU Threshold: 0.85:: 455 | 456 | CuttingMesocolon : 188.0 : 24 : 0.0 457 | PullingVasDeferens : 113.0 : 247 : 0.0 458 | ClippingVasDeferens : 48.0 : 21 : 0.0 459 | CuttingVasDeferens : 36.0 : 7 : 0.0 460 | ClippingTissue : 15.0 : 155 : 0.0 461 | PullingSeminalVesicle : 436.0 : 885 : 3.9544447959506484e-05 462 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 463 | CuttingSeminalVesicle : 307.0 : 562 : 0.0 464 | SuckingBlood : 1696.0 : 4650 : 0.0004085072575823502 465 | SuckingSmoke : 771.0 : 1299 : 2.7087346230272707e-05 466 | PullingTissue : 2024.0 : 11235 : 1.83637729293543e-05 467 | CuttingTissue : 2055.0 : 4763 : 1.976523186873731e-05 468 | BaggingProstate : 37.0 : 19 : 0.0 469 | BladderNeckDissection : 519.0 : 673 : 0.0 470 | BladderAnastomosis : 1828.0 : 10618 : 1.9342686977265854e-05 471 | PullingProstate : 451.0 : 2670 : 2.5282724061821316e-06 472 | ClippingBladderNeck : 18.0 : 157 : 0.0 473 | CuttingThread : 40.0 : 323 : 0.0 474 | UrethraDissection : 439.0 : 1007 : 2.5338201646476343e-06 475 | CuttingProstate : 48.0 : 1988 : 1.2711002643888549e-05 476 | PullingBladderNeck : 105.0 : 2473 : 0.0 477 | 478 | MEANAP:::=>0.0026 479 | 480 | 481 | 482 | IOU Threshold: 0.90:: 483 | 484 | CuttingMesocolon : 188.0 : 24 : 0.0 485 | PullingVasDeferens : 113.0 : 247 : 0.0 486 | ClippingVasDeferens : 48.0 : 21 : 0.0 487 | CuttingVasDeferens : 36.0 : 7 : 0.0 488 | ClippingTissue : 15.0 : 155 : 0.0 489 | PullingSeminalVesicle : 436.0 : 885 : 0.0 490 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 491 | CuttingSeminalVesicle : 307.0 : 562 : 0.0 492 | SuckingBlood : 1696.0 : 4650 : 5.345725322357176e-06 493 | SuckingSmoke : 771.0 : 1299 : 3.234456013015451e-06 494 | PullingTissue : 2024.0 : 11235 : 6.389651001898988e-07 495 | CuttingTissue : 2055.0 : 4763 : 3.743215422047539e-06 496 | BaggingProstate : 37.0 : 19 : 0.0 497 | BladderNeckDissection : 519.0 : 673 : 0.0 498 | BladderAnastomosis : 1828.0 : 10618 : 1.4925455953607439e-05 499 | PullingProstate : 451.0 : 2670 : 0.0 500 | ClippingBladderNeck : 18.0 : 157 : 0.0 501 | CuttingThread : 40.0 : 323 : 0.0 502 | UrethraDissection : 439.0 : 1007 : 0.0 503 | CuttingProstate : 48.0 : 1988 : 0.0 504 | PullingBladderNeck : 105.0 : 2473 : 0.0 505 | 506 | MEANAP:::=>0.0001 507 | 508 | 509 | 510 | IOU Threshold: 0.95:: 511 | 512 | CuttingMesocolon : 188.0 : 24 : 0.0 513 | PullingVasDeferens : 113.0 : 247 : 0.0 514 | ClippingVasDeferens : 48.0 : 21 : 0.0 515 | CuttingVasDeferens : 36.0 : 7 : 0.0 516 | ClippingTissue : 15.0 : 155 : 0.0 517 | PullingSeminalVesicle : 436.0 : 885 : 0.0 518 | ClippingSeminalVesicle : 33.0 : 25 : 0.0 519 | CuttingSeminalVesicle : 307.0 : 562 : 0.0 520 | SuckingBlood : 1696.0 : 4650 : 5.424311329433615e-07 521 | SuckingSmoke : 771.0 : 1299 : 0.0 522 | PullingTissue : 2024.0 : 11235 : 0.0 523 | CuttingTissue : 2055.0 : 4763 : 3.743215422047539e-06 524 | BaggingProstate : 37.0 : 19 : 0.0 525 | BladderNeckDissection : 519.0 : 673 : 0.0 526 | BladderAnastomosis : 1828.0 : 10618 : 0.0 527 | PullingProstate : 451.0 : 2670 : 0.0 528 | ClippingBladderNeck : 18.0 : 157 : 0.0 529 | CuttingThread : 40.0 : 323 : 0.0 530 | UrethraDissection : 439.0 : 1007 : 0.0 531 | CuttingProstate : 48.0 : 1988 : 0.0 532 | PullingBladderNeck : 105.0 : 2473 : 0.0 533 | 534 | MEANAP:::=>0.0000 535 | 536 | printing the on mAP results again 537 | 538 | 539 | IOU Threshold: 0.05:: 540 | 541 | 542 | MEANAP:::=>16.3969 543 | 544 | 545 | 546 | IOU Threshold: 0.10:: 547 | 548 | 549 | MEANAP:::=>16.2449 550 | 551 | 552 | 553 | IOU Threshold: 0.15:: 554 | 555 | 556 | MEANAP:::=>15.7009 557 | 558 | 559 | 560 | IOU Threshold: 0.20:: 561 | 562 | 563 | MEANAP:::=>15.0738 564 | 565 | 566 | 567 | IOU Threshold: 0.25:: 568 | 569 | 570 | MEANAP:::=>13.9052 571 | 572 | 573 | 574 | IOU Threshold: 0.30:: 575 | 576 | 577 | MEANAP:::=>12.9192 578 | 579 | 580 | 581 | IOU Threshold: 0.35:: 582 | 583 | 584 | MEANAP:::=>11.4276 585 | 586 | 587 | 588 | IOU Threshold: 0.40:: 589 | 590 | 591 | MEANAP:::=>9.6614 592 | 593 | 594 | 595 | IOU Threshold: 0.45:: 596 | 597 | 598 | MEANAP:::=>7.0887 599 | 600 | 601 | 602 | IOU Threshold: 0.50:: 603 | 604 | 605 | MEANAP:::=>5.1710 606 | 607 | 608 | 609 | IOU Threshold: 0.55:: 610 | 611 | 612 | MEANAP:::=>3.3326 613 | 614 | 615 | 616 | IOU Threshold: 0.60:: 617 | 618 | 619 | MEANAP:::=>1.8458 620 | 621 | 622 | 623 | IOU Threshold: 0.65:: 624 | 625 | 626 | MEANAP:::=>0.8610 627 | 628 | 629 | 630 | IOU Threshold: 0.70:: 631 | 632 | 633 | MEANAP:::=>0.5341 634 | 635 | 636 | 637 | IOU Threshold: 0.75:: 638 | 639 | 640 | MEANAP:::=>0.2310 641 | 642 | 643 | 644 | IOU Threshold: 0.80:: 645 | 646 | 647 | MEANAP:::=>0.0438 648 | 649 | 650 | 651 | IOU Threshold: 0.85:: 652 | 653 | 654 | MEANAP:::=>0.0026 655 | 656 | 657 | 658 | IOU Threshold: 0.90:: 659 | 660 | 661 | MEANAP:::=>0.0001 662 | 663 | 664 | 665 | IOU Threshold: 0.95:: 666 | 667 | 668 | MEANAP:::=>0.0000 669 | 670 | -------------------------------------------------------------------------------- /samples/valset.log: -------------------------------------------------------------------------------- 1 | FPN800x1440-0-esad-resnet50-hl4s0-bn1f1b1-bs16-SGD-lr010000-mbox 2 | /mnt/mercury-alpha/esad/cache/FPN800x1440-0-esad-resnet50-hl4s0-bn1f1b1-bs16-SGD-lr010000-mbox/model_006000.pth 3 | Testing net 4 | 5 | 6 | IOU Threshold: 0.05:: 7 | 8 | CuttingMesocolon : 179.0 : 185 : 0.4854562926823289 9 | PullingVasDeferens : 245.0 : 281 : 0.33327944397345766 10 | ClippingVasDeferens : 25.0 : 56 : 0.08141176470588235 11 | CuttingVasDeferens : 22.0 : 46 : 0.446969696969697 12 | ClippingTissue : 44.0 : 279 : 0.3596679725759035 13 | PullingSeminalVesicle : 342.0 : 1761 : 0.3169812586716104 14 | ClippingSeminalVesicle : 35.0 : 84 : 0.51803427809513 15 | CuttingSeminalVesicle : 196.0 : 1472 : 0.31350301044625517 16 | SuckingBlood : 575.0 : 2057 : 0.6419141801020107 17 | SuckingSmoke : 238.0 : 625 : 0.15027034356116167 18 | PullingTissue : 2177.0 : 7003 : 0.6337886098599037 19 | CuttingTissue : 1777.0 : 5781 : 0.6361005112390835 20 | BaggingProstate : 5.0 : 28 : 0.4585714285714286 21 | BladderNeckDissection : 283.0 : 735 : 0.6854227096770309 22 | BladderAnastomosis : 298.0 : 832 : 0.8695834233344653 23 | PullingProstate : 12.0 : 472 : 0.004948535233570863 24 | ClippingBladderNeck : 24.0 : 143 : 0.7740084902913851 25 | CuttingThread : 22.0 : 160 : 0.9330684187500954 26 | UrethraDissection : 56.0 : 192 : 0.011656354398289883 27 | CuttingProstate : 56.0 : 1069 : 0.06937737670810454 28 | PullingBladderNeck : 509.0 : 151 : 0.0764008999177288 29 | 30 | MEANAP:::=>41.9067 31 | 32 | 33 | 34 | IOU Threshold: 0.10:: 35 | 36 | CuttingMesocolon : 179.0 : 185 : 0.4698273611271624 37 | PullingVasDeferens : 245.0 : 281 : 0.3314478278646358 38 | ClippingVasDeferens : 25.0 : 56 : 0.08141176470588235 39 | CuttingVasDeferens : 22.0 : 46 : 0.446969696969697 40 | ClippingTissue : 44.0 : 279 : 0.3596679725759035 41 | PullingSeminalVesicle : 342.0 : 1761 : 0.31157331429068885 42 | ClippingSeminalVesicle : 35.0 : 84 : 0.51803427809513 43 | CuttingSeminalVesicle : 196.0 : 1472 : 0.31211059131749264 44 | SuckingBlood : 575.0 : 2057 : 0.6406185263668567 45 | SuckingSmoke : 238.0 : 625 : 0.15027034356116167 46 | PullingTissue : 2177.0 : 7003 : 0.6293199990889295 47 | CuttingTissue : 1777.0 : 5781 : 0.6357556506888692 48 | BaggingProstate : 5.0 : 28 : 0.4585714285714286 49 | BladderNeckDissection : 283.0 : 735 : 0.29073531706345546 50 | BladderAnastomosis : 298.0 : 832 : 0.7344602899071753 51 | PullingProstate : 12.0 : 472 : 0.004948535233570863 52 | ClippingBladderNeck : 24.0 : 143 : 0.7740084902913851 53 | CuttingThread : 22.0 : 160 : 0.41280300240231205 54 | UrethraDissection : 56.0 : 192 : 0.011656354398289883 55 | CuttingProstate : 56.0 : 1069 : 0.06937737670810454 56 | PullingBladderNeck : 509.0 : 151 : 0.0753122846025599 57 | 58 | MEANAP:::=>36.7566 59 | 60 | 61 | 62 | IOU Threshold: 0.15:: 63 | 64 | CuttingMesocolon : 179.0 : 185 : 0.4698273611271624 65 | PullingVasDeferens : 245.0 : 281 : 0.3314478278646358 66 | ClippingVasDeferens : 25.0 : 56 : 0.08141176470588235 67 | CuttingVasDeferens : 22.0 : 46 : 0.446969696969697 68 | ClippingTissue : 44.0 : 279 : 0.3596679725759035 69 | PullingSeminalVesicle : 342.0 : 1761 : 0.31091752021322283 70 | ClippingSeminalVesicle : 35.0 : 84 : 0.51803427809513 71 | CuttingSeminalVesicle : 196.0 : 1472 : 0.30710506826356726 72 | SuckingBlood : 575.0 : 2057 : 0.6406185263668567 73 | SuckingSmoke : 238.0 : 625 : 0.15027034356116167 74 | PullingTissue : 2177.0 : 7003 : 0.6266434671566655 75 | CuttingTissue : 1777.0 : 5781 : 0.632204356886957 76 | BaggingProstate : 5.0 : 28 : 0.4585714285714286 77 | BladderNeckDissection : 283.0 : 735 : 0.2239416113594677 78 | BladderAnastomosis : 298.0 : 832 : 0.45414757257653865 79 | PullingProstate : 12.0 : 472 : 0.004948535233570863 80 | ClippingBladderNeck : 24.0 : 143 : 0.5781384496640921 81 | CuttingThread : 22.0 : 160 : 0.30930681162190854 82 | UrethraDissection : 56.0 : 192 : 0.011656354398289883 83 | CuttingProstate : 56.0 : 1069 : 0.0680353061736794 84 | PullingBladderNeck : 509.0 : 151 : 0.07257678125288283 85 | 86 | MEANAP:::=>33.6021 87 | 88 | 89 | 90 | IOU Threshold: 0.20:: 91 | 92 | CuttingMesocolon : 179.0 : 185 : 0.3913698217664556 93 | PullingVasDeferens : 245.0 : 281 : 0.319960268216632 94 | ClippingVasDeferens : 25.0 : 56 : 0.08141176470588235 95 | CuttingVasDeferens : 22.0 : 46 : 0.446969696969697 96 | ClippingTissue : 44.0 : 279 : 0.3398191101237439 97 | PullingSeminalVesicle : 342.0 : 1761 : 0.30937873970181334 98 | ClippingSeminalVesicle : 35.0 : 84 : 0.51803427809513 99 | CuttingSeminalVesicle : 196.0 : 1472 : 0.29486286058519773 100 | SuckingBlood : 575.0 : 2057 : 0.6341204641428412 101 | SuckingSmoke : 238.0 : 625 : 0.15027034356116167 102 | PullingTissue : 2177.0 : 7003 : 0.6141925953844578 103 | CuttingTissue : 1777.0 : 5781 : 0.623947154281586 104 | BaggingProstate : 5.0 : 28 : 0.4585714285714286 105 | BladderNeckDissection : 283.0 : 735 : 0.20595265027737433 106 | BladderAnastomosis : 298.0 : 832 : 0.16792963338051953 107 | PullingProstate : 12.0 : 472 : 0.004948535233570863 108 | ClippingBladderNeck : 24.0 : 143 : 0.3009248422009777 109 | CuttingThread : 22.0 : 160 : 0.16380464959476798 110 | UrethraDissection : 56.0 : 192 : 0.011656354398289883 111 | CuttingProstate : 56.0 : 1069 : 0.06577676841762103 112 | PullingBladderNeck : 509.0 : 151 : 0.07108172932090029 113 | 114 | MEANAP:::=>29.4047 115 | 116 | 117 | 118 | IOU Threshold: 0.25:: 119 | 120 | CuttingMesocolon : 179.0 : 185 : 0.3101439772358803 121 | PullingVasDeferens : 245.0 : 281 : 0.319960268216632 122 | ClippingVasDeferens : 25.0 : 56 : 0.072 123 | CuttingVasDeferens : 22.0 : 46 : 0.446969696969697 124 | ClippingTissue : 44.0 : 279 : 0.31558209852778524 125 | PullingSeminalVesicle : 342.0 : 1761 : 0.29911968861056765 126 | ClippingSeminalVesicle : 35.0 : 84 : 0.4901933565933322 127 | CuttingSeminalVesicle : 196.0 : 1472 : 0.25734646014371676 128 | SuckingBlood : 575.0 : 2057 : 0.6202267797614286 129 | SuckingSmoke : 238.0 : 625 : 0.14715893496390248 130 | PullingTissue : 2177.0 : 7003 : 0.5914130224787735 131 | CuttingTissue : 1777.0 : 5781 : 0.6048907317888385 132 | BaggingProstate : 5.0 : 28 : 0.4585714285714286 133 | BladderNeckDissection : 283.0 : 735 : 0.18727237141128072 134 | BladderAnastomosis : 298.0 : 832 : 0.10357599275843657 135 | PullingProstate : 12.0 : 472 : 0.003167062549485352 136 | ClippingBladderNeck : 24.0 : 143 : 0.1768838926493701 137 | CuttingThread : 22.0 : 160 : 0.09841181156003531 138 | UrethraDissection : 56.0 : 192 : 0.011656354398289883 139 | CuttingProstate : 56.0 : 1069 : 0.05807786967593372 140 | PullingBladderNeck : 509.0 : 151 : 0.06576837789524709 141 | 142 | MEANAP:::=>26.8495 143 | 144 | 145 | 146 | IOU Threshold: 0.30:: 147 | 148 | CuttingMesocolon : 179.0 : 185 : 0.1927684200270692 149 | PullingVasDeferens : 245.0 : 281 : 0.27046764580758814 150 | ClippingVasDeferens : 25.0 : 56 : 0.072 151 | CuttingVasDeferens : 22.0 : 46 : 0.446969696969697 152 | ClippingTissue : 44.0 : 279 : 0.2576431782226624 153 | PullingSeminalVesicle : 342.0 : 1761 : 0.29241362324133713 154 | ClippingSeminalVesicle : 35.0 : 84 : 0.4789301071919382 155 | CuttingSeminalVesicle : 196.0 : 1472 : 0.23735334119087198 156 | SuckingBlood : 575.0 : 2057 : 0.5780455878507447 157 | SuckingSmoke : 238.0 : 625 : 0.13938900320475672 158 | PullingTissue : 2177.0 : 7003 : 0.5577164779647842 159 | CuttingTissue : 1777.0 : 5781 : 0.591136600301366 160 | BaggingProstate : 5.0 : 28 : 0.4585714285714286 161 | BladderNeckDissection : 283.0 : 735 : 0.1766862451082601 162 | BladderAnastomosis : 298.0 : 832 : 0.07756445327199196 163 | PullingProstate : 12.0 : 472 : 0.003167062549485352 164 | ClippingBladderNeck : 24.0 : 143 : 0.1099741756514939 165 | CuttingThread : 22.0 : 160 : 0.0337147228415304 166 | UrethraDissection : 56.0 : 192 : 0.011656354398289883 167 | CuttingProstate : 56.0 : 1069 : 0.05609367801174913 168 | PullingBladderNeck : 509.0 : 151 : 0.0603788436512517 169 | 170 | MEANAP:::=>24.2983 171 | 172 | 173 | 174 | IOU Threshold: 0.35:: 175 | 176 | CuttingMesocolon : 179.0 : 185 : 0.13154159746338515 177 | PullingVasDeferens : 245.0 : 281 : 0.24154542207570584 178 | ClippingVasDeferens : 25.0 : 56 : 0.072 179 | CuttingVasDeferens : 22.0 : 46 : 0.446969696969697 180 | ClippingTissue : 44.0 : 279 : 0.2434985521640942 181 | PullingSeminalVesicle : 342.0 : 1761 : 0.28382885966401267 182 | ClippingSeminalVesicle : 35.0 : 84 : 0.3045188130460133 183 | CuttingSeminalVesicle : 196.0 : 1472 : 0.14585779343203578 184 | SuckingBlood : 575.0 : 2057 : 0.5377177939295275 185 | SuckingSmoke : 238.0 : 625 : 0.13847541594795534 186 | PullingTissue : 2177.0 : 7003 : 0.5231306618872629 187 | CuttingTissue : 1777.0 : 5781 : 0.5606460890943193 188 | BaggingProstate : 5.0 : 28 : 0.4585714285714286 189 | BladderNeckDissection : 283.0 : 735 : 0.17461435288498406 190 | BladderAnastomosis : 298.0 : 832 : 0.05335693031356298 191 | PullingProstate : 12.0 : 472 : 0.003167062549485352 192 | ClippingBladderNeck : 24.0 : 143 : 0.05382135020080003 193 | CuttingThread : 22.0 : 160 : 0.025541125541125538 194 | UrethraDissection : 56.0 : 192 : 0.011656354398289883 195 | CuttingProstate : 56.0 : 1069 : 0.05609367801174913 196 | PullingBladderNeck : 509.0 : 151 : 0.05678288617861657 197 | 198 | MEANAP:::=>21.5397 199 | 200 | 201 | 202 | IOU Threshold: 0.40:: 203 | 204 | CuttingMesocolon : 179.0 : 185 : 0.07854446625396347 205 | PullingVasDeferens : 245.0 : 281 : 0.19387853176121145 206 | ClippingVasDeferens : 25.0 : 56 : 0.072 207 | CuttingVasDeferens : 22.0 : 46 : 0.07575757575757576 208 | ClippingTissue : 44.0 : 279 : 0.2350523379399374 209 | PullingSeminalVesicle : 342.0 : 1761 : 0.25505569553970653 210 | ClippingSeminalVesicle : 35.0 : 84 : 0.12006802721088436 211 | CuttingSeminalVesicle : 196.0 : 1472 : 0.08155132267451423 212 | SuckingBlood : 575.0 : 2057 : 0.48053124537918845 213 | SuckingSmoke : 238.0 : 625 : 0.13668512722453094 214 | PullingTissue : 2177.0 : 7003 : 0.4689576421754572 215 | CuttingTissue : 1777.0 : 5781 : 0.5315427235891896 216 | BaggingProstate : 5.0 : 28 : 0.4585714285714286 217 | BladderNeckDissection : 283.0 : 735 : 0.16760968854612318 218 | BladderAnastomosis : 298.0 : 832 : 0.042127442846229046 219 | PullingProstate : 12.0 : 472 : 0.001781472684085511 220 | ClippingBladderNeck : 24.0 : 143 : 0.031975562194008794 221 | CuttingThread : 22.0 : 160 : 0.011521287111838294 222 | UrethraDissection : 56.0 : 192 : 0.011656354398289883 223 | CuttingProstate : 56.0 : 1069 : 0.05609367801174913 224 | PullingBladderNeck : 509.0 : 151 : 0.044393956440072574 225 | 226 | MEANAP:::=>16.9303 227 | 228 | 229 | 230 | IOU Threshold: 0.45:: 231 | 232 | CuttingMesocolon : 179.0 : 185 : 0.048678340335904076 233 | PullingVasDeferens : 245.0 : 281 : 0.14298802570421598 234 | ClippingVasDeferens : 25.0 : 56 : 0.032 235 | CuttingVasDeferens : 22.0 : 46 : 0.07575757575757576 236 | ClippingTissue : 44.0 : 279 : 0.21230459652995284 237 | PullingSeminalVesicle : 342.0 : 1761 : 0.22209693345763648 238 | ClippingSeminalVesicle : 35.0 : 84 : 0.08531420959992388 239 | CuttingSeminalVesicle : 196.0 : 1472 : 0.0578208697041949 240 | SuckingBlood : 575.0 : 2057 : 0.4311261148142518 241 | SuckingSmoke : 238.0 : 625 : 0.13658393655395656 242 | PullingTissue : 2177.0 : 7003 : 0.4009282406185126 243 | CuttingTissue : 1777.0 : 5781 : 0.490335798635702 244 | BaggingProstate : 5.0 : 28 : 0.42142857142857143 245 | BladderNeckDissection : 283.0 : 735 : 0.16081009659738715 246 | BladderAnastomosis : 298.0 : 832 : 0.031402331331684914 247 | PullingProstate : 12.0 : 472 : 0.001781472684085511 248 | ClippingBladderNeck : 24.0 : 143 : 0.015742835595776772 249 | CuttingThread : 22.0 : 160 : 0.00431196100487439 250 | UrethraDissection : 56.0 : 192 : 0.011656354398289883 251 | CuttingProstate : 56.0 : 1069 : 0.05122076462173489 252 | PullingBladderNeck : 509.0 : 151 : 0.04077590899112471 253 | 254 | MEANAP:::=>14.6432 255 | 256 | 257 | 258 | IOU Threshold: 0.50:: 259 | 260 | CuttingMesocolon : 179.0 : 185 : 0.032955579717540956 261 | PullingVasDeferens : 245.0 : 281 : 0.07372666764075453 262 | ClippingVasDeferens : 25.0 : 56 : 0.008 263 | CuttingVasDeferens : 22.0 : 46 : 0.07575757575757576 264 | ClippingTissue : 44.0 : 279 : 0.19575937552112954 265 | PullingSeminalVesicle : 342.0 : 1761 : 0.16454094934921004 266 | ClippingSeminalVesicle : 35.0 : 84 : 0.05045621045621045 267 | CuttingSeminalVesicle : 196.0 : 1472 : 0.04362085600975237 268 | SuckingBlood : 575.0 : 2057 : 0.3887126457332329 269 | SuckingSmoke : 238.0 : 625 : 0.12502582393624806 270 | PullingTissue : 2177.0 : 7003 : 0.31203972486089293 271 | CuttingTissue : 1777.0 : 5781 : 0.4287492438164553 272 | BaggingProstate : 5.0 : 28 : 0.4 273 | BladderNeckDissection : 283.0 : 735 : 0.1528171834238315 274 | BladderAnastomosis : 298.0 : 832 : 0.020697302980416263 275 | PullingProstate : 12.0 : 472 : 0.001781472684085511 276 | ClippingBladderNeck : 24.0 : 143 : 0.009489693313222724 277 | CuttingThread : 22.0 : 160 : 0.00431196100487439 278 | UrethraDissection : 56.0 : 192 : 0.008910444394315362 279 | CuttingProstate : 56.0 : 1069 : 0.048868805448133795 280 | PullingBladderNeck : 509.0 : 151 : 0.025143429640713817 281 | 282 | MEANAP:::=>12.2446 283 | 284 | 285 | 286 | IOU Threshold: 0.55:: 287 | 288 | CuttingMesocolon : 179.0 : 185 : 0.025396346066737126 289 | PullingVasDeferens : 245.0 : 281 : 0.05155820385662046 290 | ClippingVasDeferens : 25.0 : 56 : 0.008 291 | CuttingVasDeferens : 22.0 : 46 : 0.045454545454545456 292 | ClippingTissue : 44.0 : 279 : 0.16205035640608467 293 | PullingSeminalVesicle : 342.0 : 1761 : 0.09786811079004892 294 | ClippingSeminalVesicle : 35.0 : 84 : 0.04230008087150944 295 | CuttingSeminalVesicle : 196.0 : 1472 : 0.020337989243559555 296 | SuckingBlood : 575.0 : 2057 : 0.3162906711211737 297 | SuckingSmoke : 238.0 : 625 : 0.11100959362024386 298 | PullingTissue : 2177.0 : 7003 : 0.2285508302131602 299 | CuttingTissue : 1777.0 : 5781 : 0.353900450859164 300 | BaggingProstate : 5.0 : 28 : 0.4 301 | BladderNeckDissection : 283.0 : 735 : 0.11914221183707811 302 | BladderAnastomosis : 298.0 : 832 : 0.01168304391197196 303 | PullingProstate : 12.0 : 472 : 0.0 304 | ClippingBladderNeck : 24.0 : 143 : 0.004629629629629629 305 | CuttingThread : 22.0 : 160 : 0.0028803217779595734 306 | UrethraDissection : 56.0 : 192 : 0.008910444394315362 307 | CuttingProstate : 56.0 : 1069 : 0.03736742029799755 308 | PullingBladderNeck : 509.0 : 151 : 0.017710235371162217 309 | 310 | MEANAP:::=>9.8335 311 | 312 | 313 | 314 | IOU Threshold: 0.60:: 315 | 316 | CuttingMesocolon : 179.0 : 185 : 0.018873622225577535 317 | PullingVasDeferens : 245.0 : 281 : 0.03160243711559082 318 | ClippingVasDeferens : 25.0 : 56 : 0.008 319 | CuttingVasDeferens : 22.0 : 46 : 0.045454545454545456 320 | ClippingTissue : 44.0 : 279 : 0.09804136554161068 321 | PullingSeminalVesicle : 342.0 : 1761 : 0.06157135045896636 322 | ClippingSeminalVesicle : 35.0 : 84 : 0.02505304219589934 323 | CuttingSeminalVesicle : 196.0 : 1472 : 0.015035791546895494 324 | SuckingBlood : 575.0 : 2057 : 0.2146762769752526 325 | SuckingSmoke : 238.0 : 625 : 0.09563800198547157 326 | PullingTissue : 2177.0 : 7003 : 0.14277547487882572 327 | CuttingTissue : 1777.0 : 5781 : 0.26607679033687504 328 | BaggingProstate : 5.0 : 28 : 0.4 329 | BladderNeckDissection : 283.0 : 735 : 0.07969962928132175 330 | BladderAnastomosis : 298.0 : 832 : 0.00757252074578854 331 | PullingProstate : 12.0 : 472 : 0.0 332 | ClippingBladderNeck : 24.0 : 143 : 0.0021367521367521365 333 | CuttingThread : 22.0 : 160 : 0.0028803217779595734 334 | UrethraDissection : 56.0 : 192 : 0.004570698119085215 335 | CuttingProstate : 56.0 : 1069 : 0.029690931558421833 336 | PullingBladderNeck : 509.0 : 151 : 0.00669055048345468 337 | 338 | MEANAP:::=>7.4097 339 | 340 | 341 | 342 | IOU Threshold: 0.65:: 343 | 344 | CuttingMesocolon : 179.0 : 185 : 0.010901404197493584 345 | PullingVasDeferens : 245.0 : 281 : 0.024034697937743685 346 | ClippingVasDeferens : 25.0 : 56 : 0.008 347 | CuttingVasDeferens : 22.0 : 46 : 0.0 348 | ClippingTissue : 44.0 : 279 : 0.08517484741440243 349 | PullingSeminalVesicle : 342.0 : 1761 : 0.030950926368907814 350 | ClippingSeminalVesicle : 35.0 : 84 : 0.010164835164835167 351 | CuttingSeminalVesicle : 196.0 : 1472 : 0.006528926989292298 352 | SuckingBlood : 575.0 : 2057 : 0.12977404490486266 353 | SuckingSmoke : 238.0 : 625 : 0.08178989252123348 354 | PullingTissue : 2177.0 : 7003 : 0.08043601145219177 355 | CuttingTissue : 1777.0 : 5781 : 0.1677540251461988 356 | BaggingProstate : 5.0 : 28 : 0.1 357 | BladderNeckDissection : 283.0 : 735 : 0.04220083305956019 358 | BladderAnastomosis : 298.0 : 832 : 0.004172412303302174 359 | PullingProstate : 12.0 : 472 : 0.0 360 | ClippingBladderNeck : 24.0 : 143 : 0.0 361 | CuttingThread : 22.0 : 160 : 0.0 362 | UrethraDissection : 56.0 : 192 : 0.004570698119085215 363 | CuttingProstate : 56.0 : 1069 : 0.022376948790901522 364 | PullingBladderNeck : 509.0 : 151 : 0.0027178971890200318 365 | 366 | MEANAP:::=>3.8645 367 | 368 | 369 | 370 | IOU Threshold: 0.70:: 371 | 372 | CuttingMesocolon : 179.0 : 185 : 0.005103427449796166 373 | PullingVasDeferens : 245.0 : 281 : 0.014400148572450162 374 | ClippingVasDeferens : 25.0 : 56 : 0.008 375 | CuttingVasDeferens : 22.0 : 46 : 0.0 376 | ClippingTissue : 44.0 : 279 : 0.04711207950410608 377 | PullingSeminalVesicle : 342.0 : 1761 : 0.013139091780178414 378 | ClippingSeminalVesicle : 35.0 : 84 : 0.002197802197802198 379 | CuttingSeminalVesicle : 196.0 : 1472 : 0.001867173432517141 380 | SuckingBlood : 575.0 : 2057 : 0.05490316583001599 381 | SuckingSmoke : 238.0 : 625 : 0.06174101627289022 382 | PullingTissue : 2177.0 : 7003 : 0.03330288822153034 383 | CuttingTissue : 1777.0 : 5781 : 0.07992988248605051 384 | BaggingProstate : 5.0 : 28 : 0.1 385 | BladderNeckDissection : 283.0 : 735 : 0.018274965647945996 386 | BladderAnastomosis : 298.0 : 832 : 0.002299440136314636 387 | PullingProstate : 12.0 : 472 : 0.0 388 | ClippingBladderNeck : 24.0 : 143 : 0.0 389 | CuttingThread : 22.0 : 160 : 0.0 390 | UrethraDissection : 56.0 : 192 : 0.003121878121878122 391 | CuttingProstate : 56.0 : 1069 : 0.01600268661728577 392 | PullingBladderNeck : 509.0 : 151 : 0.0014997225513280043 393 | 394 | MEANAP:::=>2.2043 395 | 396 | 397 | 398 | IOU Threshold: 0.75:: 399 | 400 | CuttingMesocolon : 179.0 : 185 : 0.0030197795560924054 401 | PullingVasDeferens : 245.0 : 281 : 0.00198937652898239 402 | ClippingVasDeferens : 25.0 : 56 : 0.008 403 | CuttingVasDeferens : 22.0 : 46 : 0.0 404 | ClippingTissue : 44.0 : 279 : 0.025197492824987285 405 | PullingSeminalVesicle : 342.0 : 1761 : 0.0025974021933360576 406 | ClippingSeminalVesicle : 35.0 : 84 : 0.002197802197802198 407 | CuttingSeminalVesicle : 196.0 : 1472 : 0.0005634475790900477 408 | SuckingBlood : 575.0 : 2057 : 0.020168869887484047 409 | SuckingSmoke : 238.0 : 625 : 0.029724925344874992 410 | PullingTissue : 2177.0 : 7003 : 0.012380750394417387 411 | CuttingTissue : 1777.0 : 5781 : 0.029176500209115178 412 | BaggingProstate : 5.0 : 28 : 0.1 413 | BladderNeckDissection : 283.0 : 735 : 0.007144297226345349 414 | BladderAnastomosis : 298.0 : 832 : 0.0005479625755146773 415 | PullingProstate : 12.0 : 472 : 0.0 416 | ClippingBladderNeck : 24.0 : 143 : 0.0 417 | CuttingThread : 22.0 : 160 : 0.0 418 | UrethraDissection : 56.0 : 192 : 0.002245708942763641 419 | CuttingProstate : 56.0 : 1069 : 0.002816584046867958 420 | PullingBladderNeck : 509.0 : 151 : 0.00037493063783200107 421 | 422 | MEANAP:::=>1.1816 423 | 424 | 425 | 426 | IOU Threshold: 0.80:: 427 | 428 | CuttingMesocolon : 179.0 : 185 : 0.0006823318691628641 429 | PullingVasDeferens : 245.0 : 281 : 0.0011610659301314512 430 | ClippingVasDeferens : 25.0 : 56 : 0.0 431 | CuttingVasDeferens : 22.0 : 46 : 0.0 432 | ClippingTissue : 44.0 : 279 : 0.00020475020475020476 433 | PullingSeminalVesicle : 342.0 : 1761 : 0.000282107668757787 434 | ClippingSeminalVesicle : 35.0 : 84 : 0.0006802721088435374 435 | CuttingSeminalVesicle : 196.0 : 1472 : 6.551997612283377e-05 436 | SuckingBlood : 575.0 : 2057 : 0.004752809220930628 437 | SuckingSmoke : 238.0 : 625 : 0.013931906408529251 438 | PullingTissue : 2177.0 : 7003 : 0.002732025856596648 439 | CuttingTissue : 1777.0 : 5781 : 0.0076475441032388325 440 | BaggingProstate : 5.0 : 28 : 0.1 441 | BladderNeckDissection : 283.0 : 735 : 0.0017487242079187275 442 | BladderAnastomosis : 298.0 : 832 : 0.00011716846012522964 443 | PullingProstate : 12.0 : 472 : 0.0 444 | ClippingBladderNeck : 24.0 : 143 : 0.0 445 | CuttingThread : 22.0 : 160 : 0.0 446 | UrethraDissection : 56.0 : 192 : 0.0 447 | CuttingProstate : 56.0 : 1069 : 0.0014755480607082632 448 | PullingBladderNeck : 509.0 : 151 : 0.00011871724675959936 449 | 450 | MEANAP:::=>0.6457 451 | 452 | 453 | 454 | IOU Threshold: 0.85:: 455 | 456 | CuttingMesocolon : 179.0 : 185 : 0.00012414649286157667 457 | PullingVasDeferens : 245.0 : 281 : 0.000966702470461869 458 | ClippingVasDeferens : 25.0 : 56 : 0.0 459 | CuttingVasDeferens : 22.0 : 46 : 0.0 460 | ClippingTissue : 44.0 : 279 : 0.0 461 | PullingSeminalVesicle : 342.0 : 1761 : 1.05368526421158e-05 462 | ClippingSeminalVesicle : 35.0 : 84 : 0.0 463 | CuttingSeminalVesicle : 196.0 : 1472 : 1.197662163456932e-05 464 | SuckingBlood : 575.0 : 2057 : 0.0006106215563201185 465 | SuckingSmoke : 238.0 : 625 : 0.004138508992365018 466 | PullingTissue : 2177.0 : 7003 : 0.0004001153281549748 467 | CuttingTissue : 1777.0 : 5781 : 0.0009845001969212568 468 | BaggingProstate : 5.0 : 28 : 0.1 469 | BladderNeckDissection : 283.0 : 735 : 0.0002142607518680209 470 | BladderAnastomosis : 298.0 : 832 : 0.0 471 | PullingProstate : 12.0 : 472 : 0.0 472 | ClippingBladderNeck : 24.0 : 143 : 0.0 473 | CuttingThread : 22.0 : 160 : 0.0 474 | UrethraDissection : 56.0 : 192 : 0.0 475 | CuttingProstate : 56.0 : 1069 : 0.00013401232913428035 476 | PullingBladderNeck : 509.0 : 151 : 0.0 477 | 478 | MEANAP:::=>0.5124 479 | 480 | 481 | 482 | IOU Threshold: 0.90:: 483 | 484 | CuttingMesocolon : 179.0 : 185 : 0.0 485 | PullingVasDeferens : 245.0 : 281 : 0.00010741138560687433 486 | ClippingVasDeferens : 25.0 : 56 : 0.0 487 | CuttingVasDeferens : 22.0 : 46 : 0.0 488 | ClippingTissue : 44.0 : 279 : 0.0 489 | PullingSeminalVesicle : 342.0 : 1761 : 2.63421316052895e-06 490 | ClippingSeminalVesicle : 35.0 : 84 : 0.0 491 | CuttingSeminalVesicle : 196.0 : 1472 : 0.0 492 | SuckingBlood : 575.0 : 2057 : 0.0 493 | SuckingSmoke : 238.0 : 625 : 0.003060330791423228 494 | PullingTissue : 2177.0 : 7003 : 2.906267358749044e-05 495 | CuttingTissue : 1777.0 : 5781 : 2.2581129833931095e-05 496 | BaggingProstate : 5.0 : 28 : 0.0 497 | BladderNeckDissection : 283.0 : 735 : 5.096493612394673e-05 498 | BladderAnastomosis : 298.0 : 832 : 0.0 499 | PullingProstate : 12.0 : 472 : 0.0 500 | ClippingBladderNeck : 24.0 : 143 : 0.0 501 | CuttingThread : 22.0 : 160 : 0.0 502 | UrethraDissection : 56.0 : 192 : 0.0 503 | CuttingProstate : 56.0 : 1069 : 0.0 504 | PullingBladderNeck : 509.0 : 151 : 0.0 505 | 506 | MEANAP:::=>0.0156 507 | 508 | 509 | 510 | IOU Threshold: 0.95:: 511 | 512 | CuttingMesocolon : 179.0 : 185 : 0.0 513 | PullingVasDeferens : 245.0 : 281 : 0.0 514 | ClippingVasDeferens : 25.0 : 56 : 0.0 515 | CuttingVasDeferens : 22.0 : 46 : 0.0 516 | ClippingTissue : 44.0 : 279 : 0.0 517 | PullingSeminalVesicle : 342.0 : 1761 : 0.0 518 | ClippingSeminalVesicle : 35.0 : 84 : 0.0 519 | CuttingSeminalVesicle : 196.0 : 1472 : 0.0 520 | SuckingBlood : 575.0 : 2057 : 0.0 521 | SuckingSmoke : 238.0 : 625 : 0.0 522 | PullingTissue : 2177.0 : 7003 : 0.0 523 | CuttingTissue : 1777.0 : 5781 : 0.0 524 | BaggingProstate : 5.0 : 28 : 0.0 525 | BladderNeckDissection : 283.0 : 735 : 0.0 526 | BladderAnastomosis : 298.0 : 832 : 0.0 527 | PullingProstate : 12.0 : 472 : 0.0 528 | ClippingBladderNeck : 24.0 : 143 : 0.0 529 | CuttingThread : 22.0 : 160 : 0.0 530 | UrethraDissection : 56.0 : 192 : 0.0 531 | CuttingProstate : 56.0 : 1069 : 0.0 532 | PullingBladderNeck : 509.0 : 151 : 0.0 533 | 534 | MEANAP:::=>0.0000 535 | 536 | printing the on mAP results again 537 | 538 | 539 | IOU Threshold: 0.05:: 540 | 541 | 542 | MEANAP:::=>41.9067 543 | 544 | 545 | 546 | IOU Threshold: 0.10:: 547 | 548 | 549 | MEANAP:::=>36.7566 550 | 551 | 552 | 553 | IOU Threshold: 0.15:: 554 | 555 | 556 | MEANAP:::=>33.6021 557 | 558 | 559 | 560 | IOU Threshold: 0.20:: 561 | 562 | 563 | MEANAP:::=>29.4047 564 | 565 | 566 | 567 | IOU Threshold: 0.25:: 568 | 569 | 570 | MEANAP:::=>26.8495 571 | 572 | 573 | 574 | IOU Threshold: 0.30:: 575 | 576 | 577 | MEANAP:::=>24.2983 578 | 579 | 580 | 581 | IOU Threshold: 0.35:: 582 | 583 | 584 | MEANAP:::=>21.5397 585 | 586 | 587 | 588 | IOU Threshold: 0.40:: 589 | 590 | 591 | MEANAP:::=>16.9303 592 | 593 | 594 | 595 | IOU Threshold: 0.45:: 596 | 597 | 598 | MEANAP:::=>14.6432 599 | 600 | 601 | 602 | IOU Threshold: 0.50:: 603 | 604 | 605 | MEANAP:::=>12.2446 606 | 607 | 608 | 609 | IOU Threshold: 0.55:: 610 | 611 | 612 | MEANAP:::=>9.8335 613 | 614 | 615 | 616 | IOU Threshold: 0.60:: 617 | 618 | 619 | MEANAP:::=>7.4097 620 | 621 | 622 | 623 | IOU Threshold: 0.65:: 624 | 625 | 626 | MEANAP:::=>3.8645 627 | 628 | 629 | 630 | IOU Threshold: 0.70:: 631 | 632 | 633 | MEANAP:::=>2.2043 634 | 635 | 636 | 637 | IOU Threshold: 0.75:: 638 | 639 | 640 | MEANAP:::=>1.1816 641 | 642 | 643 | 644 | IOU Threshold: 0.80:: 645 | 646 | 647 | MEANAP:::=>0.6457 648 | 649 | 650 | 651 | IOU Threshold: 0.85:: 652 | 653 | 654 | MEANAP:::=>0.5124 655 | 656 | 657 | 658 | IOU Threshold: 0.90:: 659 | 660 | 661 | MEANAP:::=>0.0156 662 | 663 | 664 | 665 | IOU Threshold: 0.95:: 666 | 667 | 668 | MEANAP:::=>0.0000 669 | 670 | --------------------------------------------------------------------------------