Example #1
0
def loss_epoch(net: nn.Module,
               dataloader: BaseDataLoader,
               loss_func: nn.Module,
               metrics: List[nn.Module],
               device: torch.device,
               optimizer: Optimizer = None,
               tqdm_description: str = None,
               max_batches: int = None) -> dict:
    """
    :param max_batches: max number of batches to process. use to perform sanity check
    """
    # create `epoch_stats` dict
    epoch_stats = {utils.get_class_name(metric): 0 for metric in metrics}
    # generate field to store loss in case it's not present in `metrics` list
    epoch_stats[utils.get_class_name(loss_func)] = 0

    n_samples = len(dataloader)
    gen = dataloader.get_generator()

    with tqdm.tqdm(total=n_samples,
                   desc=tqdm_description,
                   unit='slice',
                   leave=True,
                   bar_format=const.TQDM_BAR_FORMAT) as pbar_t:
        for batch_ix, (scans_batch, masks_batch,
                       descriptions_batch) in enumerate(gen, start=1):
            batch_size = len(scans_batch)
            batch_stats = loss_batch(net=net,
                                     x_batch=scans_batch,
                                     y_batch=masks_batch,
                                     loss_func=loss_func,
                                     metrics=metrics,
                                     device=device,
                                     optimizer=optimizer)

            # TODO: what are the issues of using `reduction='sum'`?
            #   there was one as I remember

            # add values of batch stats to epoch stats.
            # multiply batch_stats by batch_size to handle reduction='mean'
            epoch_stats = {
                k: epoch_stats[k] + batch_stats[k] * batch_size
                for k in epoch_stats.keys() & batch_stats.keys()
            }

            pbar_t.update(batch_size)

            if max_batches is not None and batch_ix >= max_batches:
                break

    # average stats
    epoch_stats = {k: v / n_samples for k, v in epoch_stats.items()}

    return epoch_stats
Example #2
0
    def __init__(self,
                 net: nn.Module,
                 loss_func: nn.Module,
                 optimizer: Optimizer,
                 train_loader: BaseDataLoader,
                 lr_min: float = 1e-8,
                 lr_max: float = 5e1,
                 beta=0.97,
                 device: torch.device = torch.device('cuda:0')):
        """
        :param beta: smoothing factor for exponentially weighted window
        """
        self.net = net
        self.loss_func = loss_func
        self.loss_name = utils.get_class_name(loss_func)
        self.optimizer = optimizer

        self.train_loader = train_loader
        self.n_batches = train_loader.n_batches

        self.lr_min = lr_min
        self.lr_max = lr_max
        self.q = (self.lr_max / self.lr_min)**(1 / (self.n_batches - 1))

        self.beta = beta
        self.device = device

        self.lrs = None
        self.losses = None
        self.raw_losses = None
Example #3
0
 def __getitem__(self, index):
     if hasattr(self, index):
         return Ref(self, index)
     else:
         class_name = get_class_name(self)[0]
         raise ValueError('%s state does not have attribute "%s".' %
                          (class_name, index))
Example #4
0
 def __getitem__(self, index):
     if hasattr(self, index):
         return Ref(self, index)
     else:
         class_name = get_class_name(self)[0]
         raise ValueError('%s state does not have attribute "%s".' % 
                          (class_name, index))
Example #5
0
 def set_vector_matrix(self, vectors):
     assert isinstance(vectors, np.ndarray), \
         'vectors must be a {} instance'.format(get_class_name(np.ndarray))
     assert vectors.shape == (self.vocab_size, self.vector_size), \
         'dimension of vectors {} mismatch with ({}, {})'.format(
             vectors.shape, self.vocab_size, self.vector_size)
     self.word2vec.syn0 = deepcopy(vectors)
     self.word2vec.syn0norm = self.word2vec.syn0
Example #6
0
 def __getitem__(self, index):
 	"""
 	Returns a reference object for the specified attribute.
 	"""
     if hasattr(self, index):
         return Ref(self, index)
     else:
         class_name = get_class_name(self)[0]
         raise ValueError('%s state does not have attribute "%s".' % 
                          (class_name, index))
Example #7
0
 def create(self):
     try:
         module = importlib.import_module('strategies.{0}'.format(self.strategy))
     except ImportError:
         logging.exception('import error')
     else:
         strategy_module = getattr(module, get_class_name(self.strategy))
         return strategy_module(
                                 initial_money = self.initial_money,
                                 initial_bet = self.initial_bet
                             )
Example #8
0
def loss_batch(net: nn.Module,
               x_batch,
               y_batch,
               loss_func: nn.Module,
               metrics: List[nn.Module],
               device: torch.device,
               optimizer: Optimizer = None) -> dict:
    batch_stats = {}
    x = torch.tensor(x_batch, dtype=torch.float, device=device).unsqueeze(1)
    y = torch.tensor(y_batch, dtype=torch.float, device=device).unsqueeze(1)

    out = net(x)
    loss = loss_func(out, y)

    loss_name = utils.get_class_name(loss_func)
    batch_stats[loss_name] = loss.item()

    if optimizer is not None:
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    # It's uncommon to set model to evaluation state with `net.eval()`
    # to calculate additional metrics during training because:
    # * it's resource expensive
    # * metrics calculated with `net.eval()` are similar to ones with `net.train()`
    # According to https://discuss.pytorch.org/t/model-eval-for-train-accuracy/61526
    # So we'll just disable gradient tracking.
    # `net.eval`() must be called outside this function when calculating metrics on validation set
    with torch.no_grad():
        for metric_func in metrics:
            metric_name = utils.get_class_name(metric_func)
            if metric_name != loss_name:
                metric_val = metric_func(out, y)
                batch_stats[metric_name] = metric_val.item()

    return batch_stats
Example #9
0
def example(img, top_k=3):
    saliency = explainer(img.to(device)).cpu().numpy()

    p, c = torch.topk(model(img.to(device)), k=top_k)
    p, c = p[0], c[0]

    plt.figure(figsize=(10, 5 * top_k))
    for k in range(top_k):
        plt.subplot(top_k, 2, 2 * k + 1)
        plt.axis('off')
        plt.title('{:.2f}% {}'.format(100 * p[k], get_class_name(c[k])))
        tensor_imshow(img[0])

        plt.subplot(top_k, 2, 2 * k + 2)
        plt.axis('off')
        plt.title(get_class_name(c[k]))
        tensor_imshow(img[0])
        sal = saliency[c[k]]
        plt.imshow(sal, cmap='jet', alpha=0.5)
        plt.colorbar(fraction=0.046, pad=0.04)

    plt.savefig('0-explain.png')
    # plt.show()
    plt.close()
Example #10
0
    def single_run(self, img_tensor, explanation, verbose=0, save_to=None):
        """Run metric on one image-saliency pair.

        Args:
            img_tensor (Tensor): normalized image tensor.
            explanation (np.ndarray): saliency map.
            verbose (int): in [0, 1, 2].
                0 - return list of scores.
                1 - also plot final step.
                2 - also plot every step and print 2 top classes.
            save_to (str): directory to save every step plots to.

        Return:
            scores (nd.array): Array containing scores at every step.
        """

        pred = self.model(img_tensor.cuda())
        top, c = torch.max(pred, 1)
        c = c.cpu().numpy()[0]
        n_steps = (HW + self.step - 1) // self.step

        if self.mode == 'del':
            title = 'Deletion game'
            ylabel = 'Pixels deleted'
            start = img_tensor.clone()
            finish = self.substrate_fn(img_tensor)
        elif self.mode == 'ins':
            title = 'Insertion game'
            ylabel = 'Pixels inserted'
            start = self.substrate_fn(img_tensor)
            finish = img_tensor.clone()

        scores = np.empty(n_steps + 1)
        # Coordinates of pixels in order of decreasing saliency
        t_r = explanation.reshape(-1, HW)
        salient_order = np.argsort(t_r, axis=1)
        salient_order = torch.flip(salient_order, [0, 1])
        for i in range(n_steps + 1):
            pred = self.model(start.cuda())
            pr, cl = torch.topk(pred, 2)
            if verbose == 2:
                print('{}: {:.3f}'.format(get_class_name(cl[0][0]),
                                          float(pr[0][0])))
                print('{}: {:.3f}'.format(get_class_name(cl[0][1]),
                                          float(pr[0][1])))
            scores[i] = pred[0, c]
            # Render image if verbose, if it's the last step or if save is required.
            if verbose == 2 or (verbose == 1 and i == n_steps) or save_to:
                plt.figure(figsize=(10, 5))
                plt.subplot(121)
                plt.title('{} {:.1f}%, P={:.4f}'.format(
                    ylabel, 100 * i / n_steps, scores[i]))
                plt.axis('off')
                tensor_imshow(start[0])

                plt.subplot(122)
                plt.plot(np.arange(i + 1) / n_steps, scores[:i + 1])
                plt.xlim(-0.1, 1.1)
                plt.ylim(0, 1.05)
                plt.fill_between(np.arange(i + 1) / n_steps,
                                 0,
                                 scores[:i + 1],
                                 alpha=0.4)
                plt.title(title)
                plt.xlabel(ylabel)
                plt.ylabel(get_class_name(c))
                if save_to:
                    plt.savefig(save_to + '/{:03d}.png'.format(i))
                    plt.close()
            if i < n_steps:
                coords = salient_order[:, self.step * i:self.step * (i + 1)]
                start.cpu().numpy().reshape(
                    1, 3,
                    HW)[0, :,
                        coords] = finish.cpu().numpy().reshape(1, 3,
                                                               HW)[0, :,
                                                                   coords]
        return auc(scores)
def main():

    ############################################################################
    # load our training data set of images examples

    program_start = cv2.getTickCount()

    print("Loading images...")
    start = cv2.getTickCount()

    # N.B. specify data path names in same order as class names (neg, pos)

    paths = [params.DATA_training_path_neg, params.DATA_training_path_pos]

    # build a list of class names automatically from our dictionary of class (name,number) pairs

    class_names = [utils.get_class_name(class_number) for class_number in range(len(params.DATA_CLASS_NAMES))]

    # specify number of sub-window samples to take from each positive and negative
    # example image in the data set
    # N.B. specify in same order as class names (neg, pos) - again

    sampling_sizes = [params.DATA_training_sample_count_neg, params.DATA_training_sample_count_pos]

    # do we want to take samples only centric to the example image or ramdonly?
    # No - for background -ve images (first class)
    # Yes - for object samples +ve images (second class)

    sample_from_centre = [False, True];

    # perform image loading

    imgs_data = utils.load_images(paths, class_names, sampling_sizes, sample_from_centre,
                            params.DATA_WINDOW_OFFSET_FOR_TRAINING_SAMPLES, params.DATA_WINDOW_SIZE);

    print(("Loaded {} image(s)".format(len(imgs_data))))
    utils.print_duration(start)

    ############################################################################
    # perform HOG feature extraction

    print("Computing HOG descriptors...") # for each training image
    start = cv2.getTickCount()
    #each HoG descriptor is stored in its respective img_data instance
    [img_data.compute_hog_descriptor() for img_data in imgs_data]
    utils.print_duration(start)

    ############################################################################
    # train an SVM based on these norm_features

    print("Training SVM...")
    start = cv2.getTickCount()

    # define SVM parameters
    svm = cv2.ml.SVM_create()
    svm.setType(cv2.ml.SVM_C_SVC)           # set SVM type
    svm.setKernel(params.HOG_SVM_kernel)    # use specific kernel type

    # get hog descriptor for each image and store in single global array
    samples = utils.get_hog_descriptors(imgs_data)

    # get class label for each training image (i.e. 0 for other, 1 for pedestrian... can extend)
    class_labels = utils.get_class_labels(imgs_data);

    # specify the termination criteria for the SVM training
    svm.setTermCriteria((cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT, params.HOG_SVM_max_training_iterations, 1.e-06))

    # perform auto training for the SVM which will essentially perform grid
    # search over the set of parameters for the chosen kernel and the penalty
    # cost term, C (N.B. trainAuto() syntax is correct as of OpenCV 3.4.x)
    svm.trainAuto(samples, cv2.ml.ROW_SAMPLE, class_labels, kFold = 10, balanced = True);

    # save the trained SVM to file so that we can load it again for testing / detection
    svm.save(params.HOG_SVM_PATH_TRAIN)

    ############################################################################
    # measure performance of the SVM trained on the bag of visual word features

    # perform prediction over the set of examples we trained over
    output = svm.predict(samples)[1].ravel()
    error = (np.absolute(class_labels.ravel() - output).sum()) / float(output.shape[0])

    # we are succesful if our prediction > than random
    # e.g. for 2 class labels this would be 1/2 = 0.5 (i.e. 50%)
    if error < (1.0 / len(params.DATA_CLASS_NAMES)):
        print("Trained SVM obtained {}% training set error".format(round(error * 100,2)))
        print("-- meaining the SVM got {}% of the training examples correct!".format(round((1.0 - error) * 100,2)))
    else:
        print("Failed to train SVM. {}% error".format(round(error * 100,2)))

    utils.print_duration(start)

    print(("Finished training HoG detector. {}".format(format_time(get_elapsed_time(program_start)))))
Example #12
0
    # Save explanations if needed.
    # explanations.tofile('exp_{:05}-{:05}.npy'.format(args.range[0], args.range[-1]))

    for i, (img, _) in enumerate(data_loader):
        p, c = torch.max(model(img.to(device)), dim=-1)
        p, c = p[0].item(), c[0].item()

        prob = torch.softmax(model(img.to(device)), dim=-1)
        pred_prob = prob[0][c]

        plt.figure(figsize=(10, 5))
        plt.suptitle('RISE Explanation for model {}'.format(args.model))

        plt.subplot(121)
        plt.axis('off')
        plt.title('{:.2f}% {}'.format(100 * p, get_class_name(c)))
        tensor_imshow(img[0])

        plt.subplot(122)
        plt.axis('off')
        plt.title(get_class_name(c))
        tensor_imshow(img[0])
        sal = explanations[i]
        plt.imshow(sal, cmap='jet', alpha=0.5)
        # plt.colorbar(fraction=0.046, pad=0.04)

        plt.savefig('{}-explain-{}.png'.format(i + 1, args.model))
        # plt.show()
        plt.close()
Example #13
0
def train_valid(net: nn.Module,
                loss_func: nn.Module,
                metrics: List[nn.Module],
                train_loader: BaseDataLoader,
                valid_loader: BaseDataLoader,
                optimizer: Optimizer,
                scheduler: ReduceLROnPlateau,
                device: torch.device,
                n_epochs: int,
                es_tolerance: float,
                es_patience: int,
                out_dp: str,
                max_batches: int = None) -> dict:
    """
    :param out_dp: path to dir where to store checkpoints, history and learning curves plot
    :param max_batches: max number of batches to process on each epoch. use to perform sanity check
    :param es_tolerance: early stopping tolerance
    :param es_patience: number of epochs with no significant improvements for early stopping to fire

    Returns
    -------
    dict with training and validation history of following structure:
    '<metric name>' : {'train': List[float], 'valid': Lists[float]}
    """
    print(const.SEPARATOR)
    print('train_valid():')

    checkpoints_dp = os.path.join(out_dp, const.MODEL_CHECKPOINTS_DN)
    os.makedirs(checkpoints_dp, exist_ok=True)

    history = {
        utils.get_class_name(m): {
            'train': [],
            'valid': []
        }
        for m in metrics
    }
    loss_name = utils.get_class_name(loss_func)

    es_cnt = 0  # early stopping counter

    best_loss_valid = float('inf')
    best_epoch_ix = -1  # index of best epoch starting from 1
    best_net_params = copy.deepcopy(net.state_dict())

    print(f'\ntrain parameters:\n\n'
          f'model architecture: {utils.get_class_name(net)}\n'
          f'loss function: {loss_name}\n'
          f'optimizer: {optimizer}\n'
          f'number of epochs: {n_epochs}\n'
          f'es tolerance: {es_tolerance : .3e}\n'
          f'es patience: {es_patience}\n'
          f'device: {device}\n'
          f'checkpoints dir: {os.path.abspath(checkpoints_dp)}\n'
          f'out dp: "{out_dp}"\n'
          f'max_batches: {max_batches}')
    print(f'\ntrain_loader:\n{train_loader}')
    print(f'\nvalid_loader:\n{valid_loader}')

    # count global time of training
    time_start_train_valid = time.time()

    for cur_epoch in range(1, n_epochs + 1):
        print(f'\n{"=" * 15} epoch {cur_epoch}/{n_epochs} {"=" * 15}')
        time_start_epoch = time.time()

        lr_on_epoch_start = get_lr_from_optimizer_first_group(optimizer)
        print(f'current learning rate: {lr_on_epoch_start : .3e}\n')

        # ----------- training ----------- #
        net.train()
        tqdm_description = f'epoch {cur_epoch}. training'

        epoch_stats_train = loss_epoch(net=net,
                                       dataloader=train_loader,
                                       loss_func=loss_func,
                                       metrics=metrics,
                                       device=device,
                                       optimizer=optimizer,
                                       tqdm_description=tqdm_description,
                                       max_batches=max_batches)

        for m_name, val in epoch_stats_train.items():
            tqdm.tqdm.write(f'training.\t{m_name}:\t{val : .4f}')
        tqdm.tqdm.write(f'time elapsed for training: '
                        f'{utils.get_elapsed_time_str(time_start_epoch)}')

        # ----------- validation ----------- #
        time_start_epoch_valid = time.time()
        net.eval()
        tqdm_description = f'epoch {cur_epoch}. validation'

        tqdm.tqdm.write('')
        with torch.no_grad():
            epoch_stats_valid = loss_epoch(
                net=net,
                dataloader=valid_loader,
                loss_func=loss_func,
                metrics=metrics,
                device=device,
                optimizer=None,  # provide no optimizer to avoid backpropagation
                tqdm_description=tqdm_description,
                max_batches=max_batches)

        for m_name, val in epoch_stats_valid.items():
            tqdm.tqdm.write(f'validation.\t{m_name}:\t{val : .4f}')
        tqdm.tqdm.write(
            f'time elapsed for validation: '
            f'{utils.get_elapsed_time_str(time_start_epoch_valid)}')

        # ----------- end of epoch ----------- #

        # store parameters
        torch.save(
            net.state_dict(),
            os.path.join(checkpoints_dp,
                         f'cp_{loss_name}_epoch_{cur_epoch:02}.pth'))

        # append epoch stats to history
        for k in epoch_stats_train.keys() & epoch_stats_valid.keys():
            history[k]['train'].append(epoch_stats_train[k])
            history[k]['valid'].append(epoch_stats_valid[k])
        last_val_loss = history[loss_name]['valid'][-1]

        # build learning curves (overwrite existing file on each epoch)
        utils.build_learning_curves(metrics=history,
                                    loss_name=loss_name,
                                    out_dp=out_dp)

        print(f'\nepoch validation loss: {last_val_loss : .4f}')

        # scheduler step
        scheduler.step(last_val_loss)
        cur_lr = get_lr_from_optimizer_first_group(optimizer)
        if cur_lr != lr_on_epoch_start:
            # load previous best parameters and continue training
            print(
                f'LR was reduced. Loading model state dict from previous best epoch:\n'
                f'best_epoch_ix: {best_epoch_ix}\n'
                f'best_loss_valid: {best_loss_valid : .4f}')
            net.load_state_dict(best_net_params)

        # print elapsed time for epoch
        print(f'\ntime elapsed for epoch: '
              f'{utils.get_elapsed_time_str(time_start_epoch)}')

        # ----------- early stopping ----------- #

        if history[loss_name]['valid'][-1] < best_loss_valid - es_tolerance:
            best_loss_valid = history[loss_name]['valid'][-1]
            tqdm.tqdm.write(
                f'\nepoch {cur_epoch}: new best loss valid: {best_loss_valid : .4f}'
            )
            best_net_params = copy.deepcopy(net.state_dict())
            best_epoch_ix = cur_epoch
            es_cnt = 0
        else:
            es_cnt += 1

        if es_cnt >= es_patience:
            tqdm.tqdm.write(const.SEPARATOR)
            tqdm.tqdm.write(
                f'\nEarly Stopping!'
                f'\nNo improvements for {es_patience} epochs for {loss_name} metric'
            )
            break

    # ----------- end of training ----------- #
    # modify history dict
    history = {
        'metrics': history,
        'loss_name': loss_name,
        'best_loss_valid': best_loss_valid,
        'best_epoch_ix': best_epoch_ix
    }

    # save best weights once again
    torch.save(best_net_params,
               os.path.join(checkpoints_dp, f'cp_{loss_name}_best.pth'))

    # load best model
    # TODO: check if weights of net outside this function are updated
    net.load_state_dict(best_net_params)

    # print summary
    print(const.SEPARATOR)
    print('end of training.')
    tqdm.tqdm.write(f'total time elapsed: '
                    f'{utils.get_elapsed_time_str(time_start_train_valid)}')
    print(f'best loss valid: {best_loss_valid : .4f}')
    print(f'best epoch: {best_epoch_ix}')

    return history
Example #14
0
    def _add_cluster(self, graph, cluster):
        # create the cluster
        name,cl_uname = get_class_name(cluster)
        clust = pydot.Cluster(cl_uname, label=name)

        # loop over children of cluster
        nodes = []
        for i,c in enumerate(cluster.children):
            if issubclass(c.__class__, ParentState):
                # call recursively
                uname,first_uname,last_uname = self._add_cluster(clust, c)

                # save in node list
                if not uname is None:
                    nodes.append({'uname':uname,
                                  'first_uname': first_uname,
                                  'last_uname': last_uname})
            else:
                # add the child node
                name,uname = get_class_name(c)
                clust.add_node(pydot.Node(uname, label=name))

                # save in node list (no children for first/last)
                nodes.append({'uname':uname,
                              'first_uname': None,
                              'last_uname': None})

            # add edges if necessary
            if issubclass(cluster.__class__, Serial) and i>0:
                # set defaults
                ledge = nodes[i-1]['uname']
                redge = nodes[i]['uname']
                ltail = None
                lhead = None
                if nodes[i-1]['last_uname']:
                    # coming from cluster
                    ledge = nodes[i-1]['last_uname']
                    ltail = nodes[i-1]['uname'].get_name()
                if nodes[i]['first_uname']:
                    # going to cluster
                    redge = nodes[i]['first_uname']
                    lhead = nodes[i]['uname'].get_name()

                if ltail and lhead:
                    self.edges.append(pydot.Edge(ledge, redge, 
                                                 ltail=ltail,
                                                 lhead=lhead))
                elif ltail:
                    self.edges.append(pydot.Edge(ledge, redge, 
                                                 ltail=ltail))
                elif lhead:
                    self.edges.append(pydot.Edge(ledge, redge, 
                                                 lhead=lhead))
                else:
                    self.edges.append(pydot.Edge(ledge, redge))

        if len(nodes) > 0:
            # insert the cluster to the graph
            graph.add_subgraph(clust)

            if nodes[0]['first_uname']:
                first_uname = nodes[0]['first_uname']
            else:
                first_uname = nodes[0]['uname']
            if nodes[-1]['last_uname']:
                last_uname = nodes[-1]['last_uname']
            else:
                last_uname = nodes[-1]['uname']
        else:
            clust, first_uname, last_uname = None,None,None

        # return the cluster uname for connections
        return clust, first_uname, last_uname
Example #15
0
    "classifier": [200, 0.5, 150, 0.5]
}

#################################################

weights_path = '/Users/idofarhi/Documents/Thesis/fold6model.pt'

model = create_model(hyper_params)

load_model(model, weights_path)
model.eval()

sum_predictions = None

for pickle_clip in pickle_clips_path.iterdir():
    if str(pickle_clip)[-9:] == '.DS_Store': continue
    prep_clip = prep_pickle_for_inference(pickle_clip)

    preds = model(prep_clip).squeeze(0)  # squeeze to remove batch dimension
    softmax_preds = torch.nn.functional.softmax(preds, dim=0)
    # print(softmax_preds)

    if sum_predictions is None:
        sum_predictions = softmax_preds
    else:
        sum_predictions += softmax_preds

print('The final prediction vector is:', sum_predictions.tolist())
predicted_class = get_class_name(sum_predictions.max(0)[1].item())
print('The final predicted class is:', predicted_class)
Example #16
0
        min_depth = 100  # initialize to then store what the closest detection is
        min_depth_class = "No Detections"  # by default in case there are no detections
        units = " meters"
        # for each detection on the image
        for i in range(len(detection_classes)):
            # get rect
            det_rect = detection_rects[i]
            # extract vertex data
            x1, y1, x2, y2 = det_rect

            # different route depending on model
            if model == "SVM":
                # get class number
                det_class = int(detection_classes[i])
                # get class name based on class number
                det_class_name = utils.get_class_name(det_class)
                # get color based on class number
                color = Colors[det_class]
            elif model == "MRCNN":
                # get class name
                det_class_name = detection_class_names[i]
                # get color based on class number
                color = Colors[detection_classes[i]]
                # get confidence
                confidence = str(round(confidences[i], 2))

            # get depth
            det_depth = round(detection_depths[i], 1)

            # draw colored rectangle where detected object is
            cv2.rectangle(imgL, (x1, y1), (x2, y2), color, 2)