Spaces:
Runtime error
Runtime error
| import json | |
| import warnings | |
| from detectron2.modeling.meta_arch.rcnn import GeneralizedRCNN | |
| from cubercnn.data.build import build_detection_train_loader | |
| warnings.filterwarnings("ignore", message="Overwriting tiny_vit_21m_512 in registry") | |
| warnings.filterwarnings("ignore", message="Overwriting tiny_vit_21m_384 in registry") | |
| warnings.filterwarnings("ignore", message="Overwriting tiny_vit_21m_224 in registry") | |
| warnings.filterwarnings("ignore", message="Overwriting tiny_vit_11m_224 in registry") | |
| warnings.filterwarnings("ignore", message="Overwriting tiny_vit_5m_224 in registry") | |
| # Copyright (c) Meta Platforms, Inc. and affiliates | |
| from contextlib import ExitStack | |
| import logging | |
| import os | |
| from detectron2.data.detection_utils import convert_image_to_rgb | |
| from detectron2.evaluation.evaluator import inference_context | |
| from detectron2.utils.visualizer import Visualizer | |
| from matplotlib import pyplot as plt | |
| import numpy as np | |
| import torch | |
| import datetime | |
| import detectron2.utils.comm as comm | |
| from detectron2.checkpoint import DetectionCheckpointer | |
| from detectron2.config import get_cfg | |
| from detectron2.data import MetadataCatalog | |
| from detectron2.engine import ( | |
| default_argument_parser, | |
| default_setup, | |
| ) | |
| from detectron2.utils.logger import setup_logger | |
| import torch.nn as nn | |
| from tqdm import tqdm | |
| import pickle | |
| from ProposalNetwork.utils.utils import show_mask2 | |
| from cubercnn.data.dataset_mapper import DatasetMapper3D | |
| from cubercnn.evaluation.omni3d_evaluation import instances_to_coco_json | |
| logger = logging.getLogger("cubercnn") | |
| from cubercnn.config import get_cfg_defaults | |
| from cubercnn.data import ( | |
| build_detection_test_loader, | |
| simple_register | |
| ) | |
| from cubercnn.evaluation import ( | |
| Omni3DEvaluationHelper, | |
| ) | |
| from cubercnn.modeling.meta_arch import build_model | |
| from cubercnn import util, vis, data | |
| # even though this import is unused, it initializes the backbone registry | |
| from cubercnn.modeling.backbone import build_dla_from_vision_fpn_backbone | |
| from matplotlib.patches import PathPatch | |
| from matplotlib.path import Path | |
| def create_striped_patch(ax, x_start, x_end, color, alpha=0.3): | |
| ylim = ax.get_ylim() | |
| stripe_height = (ylim[1] - ylim[0]) / 20 # Adjust stripe height as needed | |
| vertices = [] | |
| codes = [] | |
| for y in np.arange(ylim[0], ylim[1], stripe_height * 2): | |
| vertices.extend([(x_start, y), (x_end, y + stripe_height), (x_end, y + stripe_height * 2), (x_start, y + stripe_height)]) | |
| codes.extend([Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO]) | |
| path = Path(vertices, codes) | |
| patch = PathPatch(path, facecolor=color, edgecolor='none', alpha=alpha, hatch='/') | |
| ax.add_patch(patch) | |
| color_palette = ['#008dff','#ff73bf','#c701ff','#4ecb8d','#ff9d3a','#f0c571','#384860','#d83034'] | |
| def inference_on_dataset(model, data_loader, experiment_type, proposal_function): | |
| """ | |
| Run model on the data_loader. | |
| Also benchmark the inference speed of `model.__call__` accurately. | |
| The model will be used in eval mode. | |
| Args: | |
| model (callable): a callable which takes an object from | |
| `data_loader` and returns some outputs. | |
| If it's an nn.Module, it will be temporarily set to `eval` mode. | |
| If you wish to evaluate a model in `training` mode instead, you can | |
| wrap the given model and override its behavior of `.eval()` and `.train()`. | |
| data_loader: an iterable object with a length. | |
| The elements it generates will be the inputs to the model. | |
| Returns: | |
| The return value of `evaluator.evaluate()` | |
| """ | |
| logger.info("Start inference on {} batches".format(len(data_loader))) | |
| total = len(data_loader) # inference data loader must have a fixed length | |
| inference_json = [] | |
| with ExitStack() as stack: | |
| if isinstance(model, nn.Module): | |
| stack.enter_context(inference_context(model)) | |
| stack.enter_context(torch.no_grad()) | |
| for idx, inputs in tqdm(enumerate(data_loader), desc="Average Precision", total=total): | |
| outputs = model(inputs, experiment_type, proposal_function) | |
| for input, output in zip(inputs, outputs): | |
| prediction = { | |
| "image_id": input["image_id"], | |
| "K": input["K"], | |
| "width": input["width"], | |
| "height": input["height"], | |
| } | |
| # convert to json format | |
| instances = output["instances"].to('cpu') | |
| # instances = output["instances"].to('cpu') | |
| prediction["instances"] = instances_to_coco_json(instances, input["image_id"]) | |
| # store in overall predictions | |
| inference_json.append(prediction) | |
| return inference_json | |
| def percent_of_boxes(model, data_loader, experiment_type, proposal_functions): | |
| '''make the detection that have a certain 3D IoU score plots | |
| if you give the proposal function as input to argparser as: | |
| `PLOT.PROPOSAL_FUNC, ['random', 'z', 'xy', 'dim', 'rotation', 'aspect' ,'full']` | |
| it will work | |
| ''' | |
| total = len(data_loader) # inference data loader must have a fixed length | |
| torch.set_float32_matmul_precision('high') | |
| with ExitStack() as stack: | |
| if isinstance(model, nn.Module): | |
| stack.enter_context(inference_context(model)) | |
| stack.enter_context(torch.no_grad()) | |
| # if not os.path.exists('ProposalNetwork/output/outputs.pkl'): | |
| if True: | |
| outputs = [] | |
| for i, inputs in tqdm(enumerate(data_loader), desc=f"IoU3D plots, proposal method: {proposal_functions}", total=total): | |
| output = model(inputs, experiment_type, proposal_functions) | |
| outputs.append(output.cpu().numpy()) | |
| # with open('ProposalNetwork/output/outputs.pkl', 'wb') as f: | |
| # pickle.dump(outputs, f) | |
| # else: | |
| # with open('ProposalNetwork/output/outputs_10k.pkl', 'rb') as f: | |
| # outputs = pickle.load(f) | |
| xlim = [0.2,1] | |
| IoUat = [0.25, 0.4, 0.6] | |
| n_proposals = outputs[0].shape[-1] | |
| fig, axes = plt.subplots(1, figsize=(7.5,5)) | |
| fig2, axes2 = plt.subplots(1, len(IoUat), figsize=(20,5)) | |
| axes.set_ylabel('Detection rate') | |
| axes.set_ylim([0,1]) | |
| axes.grid(True) | |
| axes.set_xlabel('3D Intersection over Union') | |
| axes.set_xlim(xlim) | |
| axes.set_title(f'Varying proposal method, {n_proposals} proposals') | |
| for Iou, ax in zip(IoUat, axes2): | |
| ax.set_ylabel('Detection rate') | |
| ax.set_ylim([0,1]) | |
| ax.grid(True) | |
| ax.set_title(f'Variants, IoU3D = {Iou}') | |
| ax.set_xlim([1,n_proposals]) | |
| ax.set_xlabel('Number of Proposals') | |
| for k, proposal_function in enumerate(proposal_functions): | |
| IoU3Ds = np.concatenate([x[:,k,:] for x in outputs]) | |
| maxIOU_per_instance = np.max(IoU3Ds, axis=1) | |
| np.random.shuffle(IoU3Ds.T) #transpose to shuffle along the proposal axis | |
| # detection rate vs. IoU3D | |
| thresholds = np.arange(xlim[0],xlim[1],0.025) | |
| detection_rate = np.zeros(len(thresholds)) | |
| for i in range(len(thresholds)): | |
| detection_rate[i] = np.mean(maxIOU_per_instance > thresholds[i],axis=0) | |
| # detection rate vs. no. of proposals | |
| detection_rates = np.zeros((len(IoUat), IoU3Ds.shape[1])) | |
| for i, IoU in enumerate(IoUat): | |
| mask_positive_values = IoU3Ds >= IoU | |
| first_above_threshold = np.argmax(mask_positive_values, axis=1) | |
| any_above_threshold = mask_positive_values.any(axis=1) | |
| detection_rate_at_IoU = np.zeros_like(IoU3Ds) | |
| for j in range(IoU3Ds.shape[0]): | |
| if any_above_threshold[j]: | |
| detection_rate_at_IoU[j, :first_above_threshold[j]] = 0 | |
| detection_rate_at_IoU[j, first_above_threshold[j]:] = 1 | |
| detection_rates[i] = np.mean(detection_rate_at_IoU, axis=0) | |
| axes.plot(thresholds, detection_rate, label=f'{proposal_function}', color=color_palette[k]) | |
| for j, ax in enumerate(axes2): | |
| ax.semilogx(list(range(1, n_proposals+1)), detection_rates[j], label=f'{proposal_function}', color=color_palette[k]) | |
| for ax in axes2: | |
| ax.legend() | |
| axes.legend() | |
| print("saved to 'ProposalNetwork/output/'") | |
| fig.savefig(f'ProposalNetwork/output/detection_rate_{n_proposals}.png', dpi=300, bbox_inches='tight') | |
| fig2.savefig(f'ProposalNetwork/output/IoU_varying_{n_proposals}.png', dpi=300, bbox_inches='tight') | |
| return | |
| def mean_average_best_overlap(model, data_loader, experiment_type, proposal_function): | |
| total = len(data_loader) # inference data loader must have a fixed length | |
| os.makedirs('output/pkl_files', exist_ok=True) | |
| with ExitStack() as stack: | |
| if isinstance(model, nn.Module): | |
| stack.enter_context(inference_context(model)) | |
| stack.enter_context(torch.no_grad()) | |
| outputs = [] | |
| for i, inputs in tqdm(enumerate(data_loader), desc="Mean average best overlap plots", total=total): | |
| logger.info('iteration %s',i) | |
| output = model(inputs, experiment_type, proposal_function) | |
| # p_info, IoU3D, score_IoU2D, score_seg, score_dim, score_combined, score_random, score_point_cloud, stat_empty_boxes, stats_im, stats_off | |
| if output is not None: | |
| outputs.append(output) | |
| with open('output/pkl_files/outputs_'+str(proposal_function)+'.pkl', 'wb') as f: | |
| pickle.dump(outputs, f) | |
| # Create output folder | |
| if not os.path.exists('ProposalNetwork/output/MABO_'+str(proposal_function)): | |
| os.makedirs('ProposalNetwork/output/MABO_'+str(proposal_function)) # This is maybe unnecessary | |
| os.makedirs('ProposalNetwork/output/MABO_'+str(proposal_function)+'/vis/') | |
| # mean over all the outputs | |
| Iou2D = np.concatenate([np.array(sublist) for sublist in (x[1] for x in outputs)]) | |
| score_seg = np.concatenate([np.array(sublist) for sublist in (x[2] for x in outputs)]) | |
| score_dim = np.concatenate([np.array(sublist) for sublist in (x[3] for x in outputs)]) | |
| score_combined = np.concatenate([np.array(sublist) for sublist in (x[4] for x in outputs)]) | |
| score_random = np.concatenate([np.array(sublist) for sublist in (x[5] for x in outputs)]) | |
| score_point_cloud = np.concatenate([np.array(sublist) for sublist in (x[6] for x in outputs)]) | |
| score_seg_mod = np.concatenate([np.array(sublist) for sublist in (x[10] for x in outputs)]) | |
| score_corners = np.concatenate([np.array(sublist) for sublist in (x[11] for x in outputs)]) | |
| stat_empty_boxes = np.array([x[7] for x in outputs]) | |
| combinations = np.mean(np.concatenate([np.array(sublist) for sublist in (x[12] for x in outputs)]),axis=0) | |
| #logger.info('Percentage of cubes with no intersection:',np.mean(stat_empty_boxes)) | |
| print('Percentage of cubes with no intersection:',np.mean(stat_empty_boxes)) | |
| logger.info('combination scores:%s',combinations) | |
| #print('combination scores:',combinations) | |
| print('best combination is C'+str(np.argmax(combinations)+1)) | |
| Iou2D = Iou2D.mean(axis=0) | |
| score_seg = score_seg.mean(axis=0) | |
| score_dim = score_dim.mean(axis=0) | |
| score_combined = score_combined.mean(axis=0) | |
| score_random = score_random.mean(axis=0) | |
| score_point_cloud = score_point_cloud.mean(axis=0) | |
| score_seg_mod = score_seg_mod.mean(axis=0) | |
| score_corners = score_corners.mean(axis=0) | |
| total_num_instances = np.sum([x[0].gt_boxes3D.shape[0] for x in outputs]) | |
| print('Avg IoU of chosen cube:', score_combined[0]) | |
| print('Best possible IoU:', score_combined[-1]) | |
| x_range = np.arange(1,1001) | |
| plt.figure(figsize=(8,5)) | |
| plt.plot(x_range,score_combined, linestyle='-',c=color_palette[6], label='combined') | |
| plt.plot(x_range,score_dim, linestyle='-',c=color_palette[5],label='dim') | |
| plt.plot(x_range,score_seg, linestyle='-',c=color_palette[2],label='segment') | |
| plt.plot(x_range,Iou2D, linestyle='-',c=color_palette[4],label='2D IoU') | |
| plt.plot(x_range,score_corners, linestyle='-',c=color_palette[7],label='corner dist') | |
| plt.plot(x_range,score_random, linestyle='-',c='grey',label='random') | |
| plt.plot(x_range,score_point_cloud, linestyle='-',c=color_palette[3],label='point cloud') | |
| plt.plot(x_range,score_seg_mod, linestyle='-',c=color_palette[0],label='mod segment') | |
| plt.grid(True) | |
| plt.xscale('log') | |
| plt.xticks([1, 10, 100, 1000], ['1', '10', '100', '1000']) | |
| plt.xlim(left=1, right=len(Iou2D)) | |
| plt.xlabel('Number of Proposals') | |
| plt.ylabel('3D IoU') | |
| plt.legend() | |
| plt.title('Average Best Overlap vs Number of Proposals ({} images, {} instances)'.format(1+i,total_num_instances)) | |
| f_name = os.path.join('ProposalNetwork/output/MABO_'+str(proposal_function), 'MABO_'+str(proposal_function)+'.png') | |
| plt.savefig(f_name, dpi=300, bbox_inches='tight') | |
| plt.close() | |
| #logger.info('saved to ', f_name) | |
| print('saved to ', f_name) | |
| # Statistics | |
| stats = torch.cat([x[8] for x in outputs],dim=0) | |
| num_bins = 40 | |
| titles = ['x','y','z'] | |
| plt.figure(figsize=(15, 5)) | |
| plt.suptitle("Distribution of Ground Truths in Normalised Searched Range", fontsize=20) | |
| for i,title in enumerate(titles): | |
| ax = plt.subplot(1, 3, 1+i) | |
| plt.hist(stats[:,i].numpy(), bins=num_bins, color=color_palette[6],density=True, zorder=2) | |
| plt.axvline(x=0, color='#97a6c4',zorder=2) | |
| plt.axvline(x=1, color='#97a6c4',zorder=2) | |
| create_striped_patch(ax, 0, 1, '#97a6c4', alpha=0.8) | |
| plt.title(title) | |
| f_name = os.path.join('ProposalNetwork/output/MABO_'+str(proposal_function), 'stats_center_'+str(proposal_function)+'.png') | |
| plt.savefig(f_name, dpi=300, bbox_inches='tight') | |
| plt.close() | |
| print('saved to ', f_name) | |
| num_bins = 120 | |
| plt.figure(figsize=(15, 5)) | |
| plt.suptitle("Distribution of Ground Truths in Normalised Searched Range", fontsize=20) | |
| for i,title in enumerate(titles): | |
| ax = plt.subplot(1, 3, 1+i) | |
| plt.hist(stats[:,i].numpy(), bins=num_bins, color=color_palette[6],density=True, zorder=2) | |
| plt.axvline(x=0, color='#97a6c4', zorder=2) | |
| plt.axvline(x=1, color='#97a6c4', zorder=2) | |
| create_striped_patch(ax, 0, 1, '#97a6c4', alpha=0.8) | |
| plt.xlim([-2,2]) | |
| plt.title(title) | |
| f_name = os.path.join('ProposalNetwork/output/MABO_'+str(proposal_function), 'stats_center_zoom_'+str(proposal_function)+'.png') | |
| plt.savefig(f_name, dpi=300, bbox_inches='tight') | |
| plt.close() | |
| print('saved to ', f_name) | |
| num_bins = 40 | |
| titles = ['w','h','l'] | |
| plt.figure(figsize=(15, 5)) | |
| plt.suptitle("Distribution of Ground Truths in Normalised Searched Range", fontsize=20) | |
| for i,title in enumerate(titles): | |
| ax = plt.subplot(1, 3, 1+i) | |
| plt.hist(stats[:,3+i].numpy(), bins=num_bins, color=color_palette[6],density=True, zorder=2) | |
| plt.axvline(x=0, color='#97a6c4', zorder=2) | |
| plt.axvline(x=1, color='#97a6c4', zorder=2) | |
| create_striped_patch(ax, 0, 1, '#97a6c4', alpha=0.8) | |
| plt.title(title) | |
| f_name = os.path.join('ProposalNetwork/output/MABO_'+str(proposal_function), 'stats_dim_'+str(proposal_function)+'.png') | |
| plt.savefig(f_name, dpi=300, bbox_inches='tight') | |
| plt.close() | |
| print('saved to ', f_name) | |
| titles = ['rx','ry','rz'] | |
| plt.figure(figsize=(15, 5)) | |
| plt.suptitle("Distribution of Ground Truths in Normalised Searched Range", fontsize=20) | |
| for i,title in enumerate(titles): | |
| ax = plt.subplot(1, 3, 1+i) | |
| plt.hist(stats[:,6+i].numpy(), bins=num_bins, color=color_palette[6],density=True, zorder=2) | |
| plt.axvline(x=0, color='#97a6c4', zorder=2) | |
| plt.axvline(x=1, color='#97a6c4', zorder=2) | |
| create_striped_patch(ax, 0, 1, '#97a6c4', alpha=0.8) | |
| plt.title(title) | |
| f_name = os.path.join('ProposalNetwork/output/MABO_'+str(proposal_function), 'stats_rot_'+str(proposal_function)+'.png') | |
| plt.savefig(f_name, dpi=300, bbox_inches='tight') | |
| plt.close() | |
| print('saved to ', f_name) | |
| stats_off = np.concatenate([np.array(sublist) for sublist in (x[9] for x in outputs)]) | |
| plt.figure(figsize=(15, 15)) | |
| for i,title in enumerate(titles): | |
| plt.subplot(3, 3, 1+i) | |
| plt.scatter(stats_off[:,1+i],stats_off[:,0], color=color_palette[6]) | |
| plt.title(title) | |
| f_name = os.path.join('ProposalNetwork/output/MABO_'+str(proposal_function), 'stats_off_'+str(proposal_function)+'.png') | |
| plt.savefig(f_name, dpi=300, bbox_inches='tight') | |
| plt.close() | |
| #logger.info('saved to ', f_name) | |
| print('saved to ', f_name) | |
| plt.figure(figsize=(15, 15)) | |
| for i,title in enumerate(titles): | |
| plt.subplot(3, 3, 1+i) | |
| plt.scatter(stats_off[:,1+i],stats_off[:,0], color=color_palette[6]) | |
| plt.title(title) | |
| plt.xlim([0,2]) | |
| plt.ylim([0,1]) | |
| f_name = os.path.join('ProposalNetwork/output/MABO_'+str(proposal_function), 'stats_off_zoom_'+str(proposal_function)+'.png') | |
| plt.savefig(f_name, dpi=300, bbox_inches='tight') | |
| plt.close() | |
| #logger.info('saved to ', f_name) | |
| print('saved to ', f_name) | |
| # ## for vis | |
| d_iter = iter(data_loader) | |
| for i , _ in tqdm(enumerate(outputs), desc="Plotting every single image", total=len(outputs)): | |
| p_info = outputs[i][0] | |
| pred_box_classes_names = [util.MetadataCatalog.get('omni3d_model').thing_classes[label] for label in p_info.pred_cubes.labels.cpu().numpy()] | |
| box_size = p_info.pred_cubes.num_instances | |
| for x in range(box_size-len(pred_box_classes_names)): | |
| pred_box_classes_names.append(f'z={p_info.pred_cubes[x].dimensions[2]}, s={p_info.pred_cubes[x].scores}') | |
| colors = [np.concatenate([np.random.random(3), np.array([0.6])], axis=0) for _ in range(box_size)] | |
| fig, (ax, ax1) = plt.subplots(2,1, figsize=(14, 10)) | |
| input = next(d_iter)[0] | |
| images_raw = input['image'] | |
| org_img = convert_image_to_rgb(images_raw.permute(1,2,0).cpu().numpy(), 'BGR').copy() | |
| v_pred = Visualizer(org_img, None) | |
| v_pred = v_pred.overlay_instances( | |
| boxes=p_info.pred_boxes[0:box_size].tensor.cpu().numpy() | |
| , assigned_colors=colors | |
| ) | |
| prop_img = v_pred.get_image() | |
| pred_cube_meshes = [p_info.pred_cubes[j].get_cubes().__getitem__(0).detach() for j in range(box_size)] | |
| img_3DPR, img_novel, _ = vis.draw_scene_view(prop_img, p_info.K, pred_cube_meshes, text=pred_box_classes_names, blend_weight=0.5, blend_weight_overlay=0.85,scale = prop_img.shape[0],colors=colors) | |
| vis_img_3d = img_3DPR.astype(np.uint8) | |
| vis_img_3d = show_mask2(p_info.mask_per_image.cpu().numpy(), vis_img_3d, random_color=colors) # NOTE Uncomment to add segmentation mask to pred image | |
| #vis_img_3d = np.concatenate((vis_img_3d, np.zeros((vis_img_3d.shape[0],vis_img_3d.shape[1],1))), axis=-1) | |
| ax.set_title('Predicted') | |
| # expand_img_novel to have alpha channel | |
| img_novel = np.concatenate((img_novel, np.ones_like(img_novel[:,:,0:1])*255), axis=-1)/255 | |
| ax.imshow(np.concatenate((vis_img_3d, img_novel), axis=1)) | |
| box_size = len(p_info.gt_cube_meshes) | |
| v_pred = Visualizer(org_img, None) | |
| v_pred = v_pred.overlay_instances(boxes=p_info.gt_boxes[0:box_size].tensor.cpu().numpy(), assigned_colors=colors) | |
| prop_img = v_pred.get_image() | |
| gt_box_classes_names = [util.MetadataCatalog.get('omni3d_model').thing_classes[i] for i in p_info.gt_box_classes] | |
| img_3DPR, img_novel, _ = vis.draw_scene_view(prop_img, p_info.K, p_info.gt_cube_meshes,text=gt_box_classes_names, blend_weight=0.5, blend_weight_overlay=0.85,scale = prop_img.shape[0],colors=colors) | |
| vis_img_3d = img_3DPR.astype(np.uint8) | |
| im_concat = np.concatenate((vis_img_3d, img_novel), axis=1) | |
| ax1.set_title('GT') | |
| ax1.imshow(im_concat) | |
| f_name = os.path.join('ProposalNetwork/output/MABO_'+str(proposal_function)+'/vis/', f'vis_{i}.png') | |
| plt.savefig(f_name, dpi=300, bbox_inches='tight') | |
| plt.close() | |
| # with open(f'ProposalNetwork/output/MABO/vis/out_{i}.pkl', 'wb') as f: | |
| # out = images_raw.permute(1,2,0).cpu().numpy(), K, p_info.mask_per_image.cpu().numpy(), p_info.gt_boxes3D, p_info.gt_boxes[0], pred_box_classes_names | |
| # # im, K, mask, gt_boxes3D, gt_boxes, pred_box_classes_names | |
| # pickle.dump(out, f) | |
| def do_test(cfg, model, iteration='final', storage=None): | |
| filter_settings = data.get_filter_settings_from_cfg(cfg) | |
| filter_settings['visibility_thres'] = cfg.TEST.VISIBILITY_THRES | |
| filter_settings['truncation_thres'] = cfg.TEST.TRUNCATION_THRES | |
| filter_settings['min_height_thres'] = 0.0625 | |
| filter_settings['max_depth'] = 1e8 | |
| dataset_names_test = cfg.DATASETS.TEST | |
| only_2d = cfg.MODEL.ROI_CUBE_HEAD.LOSS_W_3D == 0.0 | |
| output_folder = os.path.join(cfg.OUTPUT_DIR, "inference", 'iter_{}'.format(iteration)) | |
| for dataset_name in dataset_names_test: | |
| """ | |
| Cycle through each dataset and test them individually. | |
| This loop keeps track of each per-image evaluation result, | |
| so that it doesn't need to be re-computed for the collective. | |
| """ | |
| ''' | |
| Distributed Cube R-CNN inference | |
| ''' | |
| dataset_paths = [os.path.join('datasets', 'Omni3D', name + '.json') for name in cfg.DATASETS.TEST] | |
| datasets = data.Omni3D(dataset_paths, filter_settings=filter_settings) | |
| # determine the meta data given the datasets used. | |
| data.register_and_store_model_metadata(datasets, cfg.OUTPUT_DIR, filter_settings) | |
| thing_classes = MetadataCatalog.get('omni3d_model').thing_classes | |
| dataset_id_to_contiguous_id = MetadataCatalog.get('omni3d_model').thing_dataset_id_to_contiguous_id | |
| infos = datasets.dataset['info'] | |
| if type(infos) == dict: | |
| infos = [datasets.dataset['info']] | |
| dataset_id_to_unknown_cats = {} | |
| possible_categories = set(i for i in range(cfg.MODEL.ROI_HEADS.NUM_CLASSES + 1)) | |
| dataset_id_to_src = {} | |
| for info in infos: | |
| dataset_id = info['id'] | |
| known_category_training_ids = set() | |
| if not dataset_id in dataset_id_to_src: | |
| dataset_id_to_src[dataset_id] = info['source'] | |
| for id in info['known_category_ids']: | |
| if id in dataset_id_to_contiguous_id: | |
| known_category_training_ids.add(dataset_id_to_contiguous_id[id]) | |
| # determine and store the unknown categories. | |
| unknown_categories = possible_categories - known_category_training_ids | |
| dataset_id_to_unknown_cats[dataset_id] = unknown_categories | |
| # we need the dataset mapper to get | |
| data_mapper = DatasetMapper3D(cfg, is_train=False, mode='get_depth_maps') | |
| data_mapper.dataset_id_to_unknown_cats = dataset_id_to_unknown_cats | |
| data_loader = build_detection_test_loader(cfg, dataset_name, mapper=data_mapper, batch_size=cfg.SOLVER.IMS_PER_BATCH, num_workers=4) | |
| experiment_type = {} | |
| if cfg.PLOT.EVAL == 'MABO': experiment_type['output_recall_scores'] = True | |
| else: experiment_type['output_recall_scores'] = False | |
| # either use pred_boxes or GT boxes | |
| if cfg.PLOT.MODE2D == 'PRED': experiment_type['use_pred_boxes'] = True | |
| else: experiment_type['use_pred_boxes'] = False | |
| if cfg.PLOT.SCORING_FUNC == False: | |
| experiment_type['scoring_func'] = False | |
| # define proposal function to use | |
| if experiment_type['output_recall_scores']: | |
| _ = mean_average_best_overlap(model, data_loader, experiment_type, cfg.PLOT.PROPOSAL_FUNC) | |
| elif not cfg.PLOT.SCORING_FUNC: | |
| _ = percent_of_boxes(model, data_loader, experiment_type, cfg.PLOT.PROPOSAL_FUNC) | |
| else: | |
| results_json = inference_on_dataset(model, data_loader, experiment_type, cfg.PLOT.PROPOSAL_FUNC) | |
| eval_helper = Omni3DEvaluationHelper( | |
| dataset_names_test, | |
| filter_settings, | |
| output_folder, | |
| iter_label=iteration, | |
| only_2d=only_2d, | |
| ) | |
| ''' | |
| Individual dataset evaluation | |
| ''' | |
| eval_helper.add_predictions(dataset_name, results_json) | |
| eval_helper.save_predictions(dataset_name) | |
| eval_helper.evaluate(dataset_name) | |
| ''' | |
| Optionally, visualize some instances | |
| ''' | |
| instances = torch.load(os.path.join(output_folder, dataset_name, 'instances_predictions.pth')) | |
| log_str = vis.visualize_from_instances( | |
| instances, data_loader.dataset, dataset_name, | |
| cfg.INPUT.MIN_SIZE_TEST, os.path.join(output_folder, dataset_name), | |
| MetadataCatalog.get('omni3d_model').thing_classes, iteration, visualize_every=1 | |
| ) | |
| logger.info(log_str) | |
| if cfg.PLOT.EVAL == 'AP': | |
| ''' | |
| Summarize each Omni3D Evaluation metric | |
| ''' | |
| eval_helper.summarize_all() | |
| def do_train(cfg, model): | |
| """ | |
| Run model on the data_loader. | |
| Also benchmark the inference speed of `model.__call__` accurately. | |
| The model will be used in train mode. | |
| Args: | |
| model (callable): a callable which takes an object from | |
| `data_loader` and returns some outputs. | |
| If it's an nn.Module, it will be temporarily set to `eval` mode. | |
| If you wish to evaluate a model in `training` mode instead, you can | |
| wrap the given model and override its behavior of `.eval()` and `.train()`. | |
| data_loader: an iterable object with a length. | |
| The elements it generates will be the inputs to the model. | |
| Returns: | |
| The return value of `evaluator.evaluate()` | |
| """ | |
| filter_settings = data.get_filter_settings_from_cfg(cfg) | |
| # setup and join the data. | |
| dataset_paths = [os.path.join('datasets', 'Omni3D', name + '.json') for name in cfg.DATASETS.TRAIN] | |
| datasets = data.Omni3D(dataset_paths, filter_settings=filter_settings) | |
| # determine the meta data given the datasets used. | |
| data.register_and_store_model_metadata(datasets, cfg.OUTPUT_DIR, filter_settings) | |
| thing_classes = MetadataCatalog.get('omni3d_model').thing_classes | |
| dataset_id_to_contiguous_id = MetadataCatalog.get('omni3d_model').thing_dataset_id_to_contiguous_id | |
| ''' | |
| It may be useful to keep track of which categories are annotated/known | |
| for each dataset in use, in case a method wants to use this information. | |
| ''' | |
| infos = datasets.dataset['info'] | |
| if type(infos) == dict: | |
| infos = [datasets.dataset['info']] | |
| dataset_id_to_unknown_cats = {} | |
| possible_categories = set(i for i in range(cfg.MODEL.ROI_HEADS.NUM_CLASSES + 1)) | |
| dataset_id_to_src = {} | |
| for info in infos: | |
| dataset_id = info['id'] | |
| known_category_training_ids = set() | |
| if not dataset_id in dataset_id_to_src: | |
| dataset_id_to_src[dataset_id] = info['source'] | |
| for id in info['known_category_ids']: | |
| if id in dataset_id_to_contiguous_id: | |
| known_category_training_ids.add(dataset_id_to_contiguous_id[id]) | |
| # determine and store the unknown categories. | |
| unknown_categories = possible_categories - known_category_training_ids | |
| dataset_id_to_unknown_cats[dataset_id] = unknown_categories | |
| # we need the dataset mapper to get | |
| dataset_names = cfg.DATASETS.TRAIN | |
| data_mapper = DatasetMapper3D(cfg, is_train=False, mode='get_depth_maps') | |
| data_mapper.dataset_id_to_unknown_cats = dataset_id_to_unknown_cats | |
| assert cfg.TRAIN.pseudo_gt in ['learn', 'pseudo'], "control what kind of proposal should be saved by setting TRAIN.pseudo_gt to either 'learn' or 'pseudo'" | |
| experiment_type = {} | |
| experiment_type['use_pred_boxes'] = cfg.PLOT.MODE2D if cfg.PLOT.MODE2D != '' else False | |
| experiment_type['pseudo_gt'] = cfg.TRAIN.pseudo_gt | |
| os.makedirs(f'datasets/proposals_{cfg.TRAIN.pseudo_gt}',exist_ok=True) | |
| # lol I think we have to hardcode this part in | |
| dataset_json = {} | |
| dataset_json.update({"categories": [{"supercategory": "nan", "id": 18, "name": "chair"}, {"supercategory": "nan", "id": 31, "name": "door"}, {"supercategory": "nan", "id": 37, "name": "table"}, {"supercategory": "nan", "id": 26, "name": "shelves"}, {"supercategory": "nan", "id": 51, "name": "kitchen pan"}, {"supercategory": "nan", "id": 52, "name": "bin"}, {"supercategory": "nan", "id": 38, "name": "counter"}, {"supercategory": "nan", "id": 29, "name": "cabinet"}, {"supercategory": "nan", "id": 53, "name": "stove"}, {"supercategory": "nan", "id": 28, "name": "sink"}, {"supercategory": "nan", "id": 14, "name": "books"}, {"supercategory": "nan", "id": 49, "name": "refrigerator"}, {"supercategory": "nan", "id": 54, "name": "microwave"}, {"supercategory": "nan", "id": 15, "name": "bottle"}, {"supercategory": "nan", "id": 55, "name": "plates"}, {"supercategory": "nan", "id": 56, "name": "bowl"}, {"supercategory": "nan", "id": 57, "name": "oven"}, {"supercategory": "nan", "id": 58, "name": "vase"}, {"supercategory": "nan", "id": 59, "name": "faucet"}, {"supercategory": "nan", "id": 22, "name": "towel"}, {"supercategory": "nan", "id": 60, "name": "tissues"}, {"supercategory": "nan", "id": 61, "name": "machine"}, {"supercategory": "nan", "id": 62, "name": "printer"}, {"supercategory": "nan", "id": 33, "name": "desk"}, {"supercategory": "nan", "id": 63, "name": "monitor"}, {"supercategory": "nan", "id": 64, "name": "podium"}, {"supercategory": "nan", "id": 35, "name": "bookcase"}, {"supercategory": "nan", "id": 41, "name": "dresser"}, {"supercategory": "nan", "id": 65, "name": "cart"}, {"supercategory": "nan", "id": 66, "name": "projector"}, {"supercategory": "nan", "id": 67, "name": "electronics"}, {"supercategory": "nan", "id": 68, "name": "computer"}, {"supercategory": "nan", "id": 34, "name": "box"}, {"supercategory": "nan", "id": 36, "name": "picture"}, {"supercategory": "nan", "id": 20, "name": "laptop"}, {"supercategory": "nan", "id": 42, "name": "pillow"}, {"supercategory": "nan", "id": 39, "name": "bed"}, {"supercategory": "nan", "id": 69, "name": "air conditioner"}, {"supercategory": "nan", "id": 25, "name": "lamp"}, {"supercategory": "nan", "id": 40, "name": "night stand"}, {"supercategory": "nan", "id": 50, "name": "board"}, {"supercategory": "nan", "id": 43, "name": "sofa"}, {"supercategory": "nan", "id": 71, "name": "coffee maker"}, {"supercategory": "nan", "id": 72, "name": "toaster"}, {"supercategory": "nan", "id": 73, "name": "potted plant"}, {"supercategory": "nan", "id": 48, "name": "stationery"}, {"supercategory": "nan", "id": 74, "name": "painting"}, {"supercategory": "nan", "id": 75, "name": "bag"}, {"supercategory": "nan", "id": 76, "name": "tray"}, {"supercategory": "nan", "id": 19, "name": "cup"}, {"supercategory": "nan", "id": 70, "name": "drawers"}, {"supercategory": "nan", "id": 77, "name": "keyboard"}, {"supercategory": "nan", "id": 21, "name": "shoes"}, {"supercategory": "vehicle & road", "id": 11, "name": "bicycle"}, {"supercategory": "nan", "id": 78, "name": "blanket"}, {"supercategory": "nan", "id": 44, "name": "television"}, {"supercategory": "nan", "id": 79, "name": "rack"}, {"supercategory": "nan", "id": 27, "name": "mirror"}, {"supercategory": "nan", "id": 47, "name": "clothes"}, {"supercategory": "nan", "id": 80, "name": "phone"}, {"supercategory": "nan", "id": 81, "name": "mouse"}, {"supercategory": "person", "id": 7, "name": "person"}, {"supercategory": "nan", "id": 82, "name": "fire extinguisher"}, {"supercategory": "nan", "id": 83, "name": "toys"}, {"supercategory": "nan", "id": 84, "name": "ladder"}, {"supercategory": "nan", "id": 85, "name": "fan"}, {"supercategory": "nan", "id": 32, "name": "toilet"}, {"supercategory": "nan", "id": 30, "name": "bathtub"}, {"supercategory": "nan", "id": 86, "name": "glass"}, {"supercategory": "nan", "id": 87, "name": "clock"}, {"supercategory": "nan", "id": 88, "name": "toilet paper"}, {"supercategory": "nan", "id": 89, "name": "closet"}, {"supercategory": "nan", "id": 46, "name": "curtain"}, {"supercategory": "nan", "id": 24, "name": "window"}, {"supercategory": "nan", "id": 90, "name": "fume hood"}, {"supercategory": "nan", "id": 91, "name": "utensils"}, {"supercategory": "nan", "id": 45, "name": "floor mat"}, {"supercategory": "nan", "id": 92, "name": "soundsystem"}, {"supercategory": "nan", "id": 93, "name": "fire place"}, {"supercategory": "nan", "id": 94, "name": "shower curtain"}, {"supercategory": "nan", "id": 23, "name": "blinds"}, {"supercategory": "nan", "id": 95, "name": "remote"}, {"supercategory": "nan", "id": 96, "name": "pen"}]}) | |
| d_id_to_contiguous = MetadataCatalog.get('omni3d_model').thing_dataset_id_to_contiguous_id | |
| contiguous_to_id = {v:k for k,v in d_id_to_contiguous.items()} | |
| global_id = 1 | |
| # this controls the flow of the program in the model class | |
| model.train() | |
| for dataset_name in dataset_names: | |
| idd = 12 | |
| if 'val' in dataset_name: | |
| idd = 13 | |
| dataset_json.update({"info": {"id": idd, "source": "SUNRGBD", "name": "SUNRGBD Train", "split": "Train", "version": "0.1", "url": "https://rgbd.cs.princeton.edu/"}}) | |
| data_loader = build_detection_test_loader(cfg, dataset_name, mapper=data_mapper, num_workers=4) | |
| total = len(data_loader) # inference data loader must have a fixed length | |
| annotations = [] | |
| images = [] | |
| for idx, inputs in tqdm(enumerate(data_loader), desc="Generating pseudo GT", total=total): | |
| cubes = model(inputs, experiment_type) | |
| instances = cubes[0]['instances'] | |
| input_ = inputs[0] | |
| img_id = input_['image_id'] | |
| input_['instances'].proposal_boxes = input_['instances'].gt_boxes | |
| bboxes = GeneralizedRCNN._postprocess([input_['instances']], [input_], [input_['instances']._image_size]) | |
| bboxes = bboxes[0]['instances'].proposal_boxes | |
| # build json for each image | |
| img = {'width':input_['width'], 'height':input_['height'], 'file_path':input_['file_name'][9:], 'K':input_['K'], 'src_90_rotate':False, 'src_flagged':False, 'incomplete':False, 'id':img_id, 'dataset_id':12} | |
| for bbox, gt_class, center, dimensions, bbox3D, rotation in zip(bboxes, input_['instances'].gt_classes, | |
| instances.pred_center_cam.tolist(), instances.pred_dimensions.tolist(), | |
| instances.pred_bbox3D.tolist(), instances.pred_pose.tolist()): | |
| c_id = util.MetadataCatalog.get('omni3d_model').thing_classes[gt_class] | |
| ann = {'behind_camera':False, 'truncation': 0, 'bbox2D_proj':bbox.tolist(), 'bbox2D_tight':-1, 'visibility':1.0, 'segmentation_pts':-1, 'lidar_pts':-1,\ | |
| 'valid3D':True, 'category_id':contiguous_to_id[gt_class.tolist()], 'category_name':c_id, \ | |
| 'id':global_id, 'image_id':img_id, 'dataset_id':idd, 'depth_error':-1, 'center_cam':center,\ | |
| 'dimensions':dimensions, 'bbox3D_cam':bbox3D, 'R_cam':rotation} | |
| annotations.append(ann) | |
| global_id += 1 | |
| images.append(img) | |
| dataset_json.update({'images':images, 'annotations':annotations}) | |
| with open(f'datasets/Omni3D/SUNRGBD_pseudo_gt_{dataset_name}.json', 'w') as f: | |
| json.dump(dataset_json, f) | |
| return True | |
| def setup(args): | |
| """ | |
| Create configs and perform basic setups. | |
| """ | |
| cfg = get_cfg() | |
| get_cfg_defaults(cfg) | |
| config_file = args.config_file | |
| # store locally if needed | |
| if config_file.startswith(util.CubeRCNNHandler.PREFIX): | |
| config_file = util.CubeRCNNHandler._get_local_path(util.CubeRCNNHandler, config_file) | |
| cfg.merge_from_file(config_file) | |
| cfg.merge_from_list(args.opts) | |
| device = "cuda" if torch.cuda.is_available() else "cpu" | |
| cfg.MODEL.DEVICE = device | |
| cfg.SEED = 13 | |
| cfg.freeze() | |
| default_setup(cfg, args) | |
| setup_logger(output=cfg.OUTPUT_DIR, distributed_rank=comm.get_rank(), name="cubercnn") | |
| filter_settings = data.get_filter_settings_from_cfg(cfg) | |
| for dataset_name in cfg.DATASETS.TRAIN: | |
| simple_register(dataset_name, filter_settings, filter_empty=True) | |
| dataset_names_test = cfg.DATASETS.TEST | |
| # filter_ = True if cfg.PLOT.EVAL == 'MABO' else False | |
| for dataset_name in dataset_names_test: | |
| if not(dataset_name in cfg.DATASETS.TRAIN): | |
| # empties should be filtering in test normally | |
| simple_register(dataset_name, filter_settings, filter_empty=True) | |
| return cfg | |
| def main(args): | |
| cfg = setup(args) | |
| if args.eval_only: | |
| assert cfg.PLOT.MODE2D in ['GT', 'PRED'], 'MODE2D must be either GT or PRED' | |
| assert cfg.PLOT.EVAL in ['AP', 'MABO', 'IoU3D'], 'EVAL must be either AP, MABO or IoU3D' | |
| if cfg.PLOT.EVAL == 'MABO': | |
| assert cfg.PLOT.MODE2D == 'GT', 'MABO only works with GT boxes' | |
| name = f'cube {datetime.datetime.now().isoformat()}' | |
| # wandb.init(project="cube", sync_tensorboard=True, name=name, config=cfg) | |
| priors = None | |
| with open('tools/priors.pkl', 'rb') as f: | |
| priors, _ = pickle.load(f) | |
| category_path = 'output/Baseline_sgd/category_meta.json' | |
| # category_path = os.path.join(util.file_parts(args.opts[1])[0], 'category_meta.json') | |
| # store locally if needed | |
| if category_path.startswith(util.CubeRCNNHandler.PREFIX): | |
| category_path = util.CubeRCNNHandler._get_local_path(util.CubeRCNNHandler, category_path) | |
| metadata = util.load_json(category_path) | |
| # register the categories | |
| thing_classes = metadata['thing_classes'] | |
| id_map = {int(key):val for key, val in metadata['thing_dataset_id_to_contiguous_id'].items()} | |
| MetadataCatalog.get('omni3d_model').thing_classes = thing_classes | |
| MetadataCatalog.get('omni3d_model').thing_dataset_id_to_contiguous_id = id_map | |
| # build the model. | |
| model = build_model(cfg, priors=priors) | |
| if args.eval_only: | |
| # skip straight to eval mode | |
| # load the saved model if using eval boxes | |
| if cfg.PLOT.MODE2D == 'PRED': | |
| DetectionCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load( | |
| cfg.MODEL.WEIGHTS, resume=False) | |
| return do_test(cfg, model) | |
| else: | |
| logger.info('Making pseudo GT') | |
| return do_train(cfg, model) | |
| if __name__ == "__main__": | |
| args = default_argument_parser().parse_args() | |
| print("Command Line Args:", args) | |
| main(args) |