diff --git a/lib/core/rpn_generator.py b/lib/core/rpn_generator.py index aaf9ed86e..42555ba6a 100644 --- a/lib/core/rpn_generator.py +++ b/lib/core/rpn_generator.py @@ -78,8 +78,8 @@ def multi_gpu_generate_rpn_on_dataset(num_images, output_dir): # Retrieve the test_net binary path binary_dir = envu.get_runtime_dir() binary_ext = envu.get_py_bin_ext() - binary = os.path.join(binary_dir, 'test_net' + binary_ext) - assert os.path.exists(binary), 'Binary \'{}\' not found'.format(binary) + binary = os.path.join(binary_dir, f'test_net{binary_ext}') + assert os.path.exists(binary), f"Binary '{binary}' not found" # Run inference in parallel in subprocesses outputs = subprocess_utils.process_in_parallel( @@ -97,7 +97,7 @@ def multi_gpu_generate_rpn_on_dataset(num_images, output_dir): save_object( dict(boxes=boxes, scores=scores, ids=ids, cfg=cfg_yaml), rpn_file ) - logger.info('Wrote RPN proposals to {}'.format(os.path.abspath(rpn_file))) + logger.info(f'Wrote RPN proposals to {os.path.abspath(rpn_file)}') return boxes, scores, ids, rpn_file @@ -139,7 +139,7 @@ def generate_rpn_on_range(ind_range=None): save_object( dict(boxes=boxes, scores=scores, ids=ids, cfg=cfg_yaml), rpn_file ) - logger.info('Wrote RPN proposals to {}'.format(os.path.abspath(rpn_file))) + logger.info(f'Wrote RPN proposals to {os.path.abspath(rpn_file)}') return boxes, scores, ids, rpn_file @@ -193,13 +193,15 @@ def im_proposals(model, im): k_max = cfg.FPN.RPN_MAX_LEVEL k_min = cfg.FPN.RPN_MIN_LEVEL rois_names = [ - core.ScopedName('rpn_rois_fpn' + str(l)) + core.ScopedName(f'rpn_rois_fpn{str(l)}') for l in range(k_min, k_max + 1) ] + score_names = [ - core.ScopedName('rpn_roi_probs_fpn' + str(l)) + core.ScopedName(f'rpn_roi_probs_fpn{str(l)}') for l in range(k_min, k_max + 1) ] + blobs = workspace.FetchBlobs(rois_names + score_names) # Combine predictions across all levels and retain the top scoring boxes = np.concatenate(blobs[:len(rois_names)]) @@ -269,10 +271,8 @@ def _get_image_blob(im): im_orig -= cfg.PIXEL_MEANS im_shape = im_orig.shape - im_size_min = np.min(im_shape[0:2]) - im_size_max = np.max(im_shape[0:2]) - - processed_ims = [] + im_size_min = np.min(im_shape[:2]) + im_size_max = np.max(im_shape[:2]) assert len(cfg.TEST.SCALES) == 1 target_size = cfg.TEST.SCALES[0] @@ -284,8 +284,7 @@ def _get_image_blob(im): im = cv2.resize(im_orig, None, None, fx=im_scale, fy=im_scale, interpolation=cv2.INTER_LINEAR) im_info = np.hstack((im.shape[:2], im_scale))[np.newaxis, :] - processed_ims.append(im) - + processed_ims = [im] # Create a blob to hold the input images blob = im_list_to_blob(processed_ims) diff --git a/lib/core/test.py b/lib/core/test.py index bf788afad..fe2ba2e73 100644 --- a/lib/core/test.py +++ b/lib/core/test.py @@ -291,10 +291,11 @@ def im_detect_bbox_hflip(model, im, box_proposals=None): im_hf = im[:, ::-1, :] im_width = im.shape[1] - if not cfg.MODEL.FASTER_RCNN: - box_proposals_hf = box_utils.flip_boxes(box_proposals, im_width) - else: - box_proposals_hf = None + box_proposals_hf = ( + None + if cfg.MODEL.FASTER_RCNN + else box_utils.flip_boxes(box_proposals, im_width) + ) scores_hf, boxes_hf, im_scales = im_detect_bbox( model, im_hf, box_proposals_hf @@ -343,10 +344,11 @@ def im_detect_bbox_aspect_ratio( # Compute predictions on the transformed image im_ar = image_utils.aspect_ratio_rel(im, aspect_ratio) - if not cfg.MODEL.FASTER_RCNN: - box_proposals_ar = box_utils.aspect_ratio(box_proposals, aspect_ratio) - else: - box_proposals_ar = None + box_proposals_ar = ( + None + if cfg.MODEL.FASTER_RCNN + else box_utils.aspect_ratio(box_proposals, aspect_ratio) + ) if hflip: scores_ar, boxes_ar, _ = im_detect_bbox_hflip( @@ -489,10 +491,7 @@ def im_detect_mask_hflip(model, im, boxes): im_scales = im_conv_body_only(model, im_hf) masks_hf = im_detect_mask(model, im_scales, boxes_hf) - # Invert the predicted soft masks - masks_inv = masks_hf[:, :, :, ::-1] - - return masks_inv + return masks_hf[:, :, :, ::-1] def im_detect_mask_scale(model, im, scale, max_size, boxes, hflip=False): @@ -527,12 +526,9 @@ def im_detect_mask_aspect_ratio(model, im, aspect_ratio, boxes, hflip=False): boxes_ar = box_utils.aspect_ratio(boxes, aspect_ratio) if hflip: - masks_ar = im_detect_mask_hflip(model, im_ar, boxes_ar) - else: - im_scales = im_conv_body_only(model, im_ar) - masks_ar = im_detect_mask(model, im_scales, boxes_ar) - - return masks_ar + return im_detect_mask_hflip(model, im_ar, boxes_ar) + im_scales = im_conv_body_only(model, im_ar) + return im_detect_mask(model, im_scales, boxes_ar) def im_detect_keypoints(model, im_scales, boxes): @@ -675,10 +671,7 @@ def im_detect_keypoints_hflip(model, im, boxes): im_scales = im_conv_body_only(model, im_hf) heatmaps_hf = im_detect_keypoints(model, im_scales, boxes_hf) - # Invert the predicted keypoints - heatmaps_inv = keypoint_utils.flip_heatmaps(heatmaps_hf) - - return heatmaps_inv + return keypoint_utils.flip_heatmaps(heatmaps_hf) def im_detect_keypoints_scale(model, im, scale, max_size, boxes, hflip=False): @@ -715,18 +708,17 @@ def im_detect_keypoints_aspect_ratio( boxes_ar = box_utils.aspect_ratio(boxes, aspect_ratio) if hflip: - heatmaps_ar = im_detect_keypoints_hflip(model, im_ar, boxes_ar) - else: - im_scales = im_conv_body_only(model, im_ar) - heatmaps_ar = im_detect_keypoints(model, im_scales, boxes_ar) - - return heatmaps_ar + return im_detect_keypoints_hflip(model, im_ar, boxes_ar) + im_scales = im_conv_body_only(model, im_ar) + return im_detect_keypoints(model, im_scales, boxes_ar) def combine_heatmaps_size_dep(hms_ts, ds_ts, us_ts, boxes, heur_f): """Combines heatmaps while taking object sizes into account.""" - assert len(hms_ts) == len(ds_ts) and len(ds_ts) == len(us_ts), \ - 'All sets of hms must be tagged with downscaling and upscaling flags' + assert ( + len(hms_ts) == len(ds_ts) == len(us_ts) + ), 'All sets of hms must be tagged with downscaling and upscaling flags' + # Classify objects into small+medium and large based on their box areas areas = box_utils.boxes_area(boxes) diff --git a/lib/core/test_engine.py b/lib/core/test_engine.py index 261160d03..b0894493c 100644 --- a/lib/core/test_engine.py +++ b/lib/core/test_engine.py @@ -62,18 +62,17 @@ def test_net_on_dataset(multi_gpu=False): all_boxes, all_segms, all_keyps = test_net() test_timer.toc() logger.info('Total inference time: {:.3f}s'.format(test_timer.average_time)) - results = task_evaluation.evaluate_all( + return task_evaluation.evaluate_all( dataset, all_boxes, all_segms, all_keyps, output_dir ) - return results def multi_gpu_test_net_on_dataset(num_images, output_dir): """Multi-gpu inference on a dataset.""" binary_dir = envu.get_runtime_dir() binary_ext = envu.get_py_bin_ext() - binary = os.path.join(binary_dir, 'test_net' + binary_ext) - assert os.path.exists(binary), 'Binary \'{}\' not found'.format(binary) + binary = os.path.join(binary_dir, f'test_net{binary_ext}') + assert os.path.exists(binary), f"Binary '{binary}' not found" # Run inference in parallel in subprocesses # Outputs will be a list of outputs from each subprocess, where the output @@ -104,7 +103,7 @@ def multi_gpu_test_net_on_dataset(num_images, output_dir): cfg=cfg_yaml ), det_file ) - logger.info('Wrote detections to: {}'.format(os.path.abspath(det_file))) + logger.info(f'Wrote detections to: {os.path.abspath(det_file)}') return all_boxes, all_segms, all_keyps @@ -208,7 +207,7 @@ def test_net(ind_range=None): cfg=cfg_yaml ), det_file ) - logger.info('Wrote detections to: {}'.format(os.path.abspath(det_file))) + logger.info(f'Wrote detections to: {os.path.abspath(det_file)}') return all_boxes, all_segms, all_keyps diff --git a/lib/datasets/cityscapes/tools/convert_cityscapes_to_coco.py b/lib/datasets/cityscapes/tools/convert_cityscapes_to_coco.py index 1a3d6bf2f..58b2cbba8 100644 --- a/lib/datasets/cityscapes/tools/convert_cityscapes_to_coco.py +++ b/lib/datasets/cityscapes/tools/convert_cityscapes_to_coco.py @@ -45,38 +45,39 @@ def convert_coco_stuff_mat(data_dir, out_dir): with open(file_list % data_set) as f: for img_id, img_name in enumerate(f): img_name = img_name.replace('coco', 'COCO').strip('\n') - image = {} - mat_file = os.path.join( - data_dir, 'annotations/%s.mat' % img_name) + mat_file = os.path.join(data_dir, f'annotations/{img_name}.mat') data = h5py.File(mat_file, 'r') labelMap = data.get('S') - if len(categories) == 0: + if not categories: labelNames = data.get('names') - for idx, n in enumerate(labelNames): - categories.append( - {"id": idx, "name": ''.join(chr(i) for i in data[ - n[0]])}) + categories.extend( + { + "id": idx, + "name": ''.join(chr(i) for i in data[n[0]]), + } + for idx, n in enumerate(labelNames) + ) + ann_dict['categories'] = categories - scipy.misc.imsave( - os.path.join(data_dir, img_name + '.png'), labelMap) - image['width'] = labelMap.shape[0] - image['height'] = labelMap.shape[1] - image['file_name'] = img_name - image['seg_file_name'] = img_name - image['id'] = img_id + scipy.misc.imsave(os.path.join(data_dir, f'{img_name}.png'), labelMap) + image = { + 'width': labelMap.shape[0], + 'height': labelMap.shape[1], + 'file_name': img_name, + 'seg_file_name': img_name, + 'id': img_id, + } + images.append(image) ann_dict['images'] = images - print("Num images: %s" % len(images)) + print(f"Num images: {len(images)}") with open(os.path.join(out_dir, json_name % data_set), 'wb') as outfile: outfile.write(json.dumps(ann_dict)) # for Cityscapes def getLabelID(self, instID): - if (instID < 1000): - return instID - else: - return int(instID / 1000) + return instID if (instID < 1000) else int(instID / 1000) def convert_cityscapes_instance_only( @@ -119,8 +120,7 @@ def convert_cityscapes_instance_only( ] for data_set, ann_dir in zip(sets, ann_dirs): - print('Starting %s' % data_set) - ann_dict = {} + print(f'Starting {data_set}') images = [] annotations = [] ann_dir = os.path.join(data_dir, ann_dir) @@ -128,20 +128,20 @@ def convert_cityscapes_instance_only( for filename in files: if filename.endswith(ends_in % data_set.split('_')[0]): if len(images) % 50 == 0: - print("Processed %s images, %s annotations" % ( - len(images), len(annotations))) + print(f"Processed {len(images)} images, {len(annotations)} annotations") json_ann = json.load(open(os.path.join(root, filename))) - image = {} - image['id'] = img_id + image = {'id': img_id} img_id += 1 image['width'] = json_ann['imgWidth'] image['height'] = json_ann['imgHeight'] image['file_name'] = filename[:-len( ends_in % data_set.split('_')[0])] + 'leftImg8bit.png' - image['seg_file_name'] = filename[:-len( - ends_in % data_set.split('_')[0])] + \ - '%s_instanceIds.png' % data_set.split('_')[0] + image['seg_file_name'] = ( + filename[: -len(ends_in % data_set.split('_')[0])] + + f"{data_set.split('_')[0]}_instanceIds.png" + ) + images.append(image) fullname = os.path.join(root, image['seg_file_name']) @@ -162,8 +162,7 @@ def convert_cityscapes_instance_only( print('Warning: invalid contours.') continue # skip non-instance categories - ann = {} - ann['id'] = ann_id + ann = {'id': ann_id} ann_id += 1 ann['image_id'] = image['id'] ann['segmentation'] = obj['contours'] @@ -180,14 +179,14 @@ def convert_cityscapes_instance_only( annotations.append(ann) - ann_dict['images'] = images + ann_dict = {'images': images} categories = [{"id": category_dict[name], "name": name} for name in category_dict] ann_dict['categories'] = categories ann_dict['annotations'] = annotations - print("Num categories: %s" % len(categories)) - print("Num images: %s" % len(images)) - print("Num annotations: %s" % len(annotations)) + print(f"Num categories: {len(categories)}") + print(f"Num images: {len(images)}") + print(f"Num annotations: {len(annotations)}") with open(os.path.join(out_dir, json_name % data_set), 'wb') as outfile: outfile.write(json.dumps(ann_dict)) @@ -199,4 +198,4 @@ def convert_cityscapes_instance_only( elif args.dataset == "cocostuff": convert_coco_stuff_mat(args.datadir, args.outdir) else: - print("Dataset not supported: %s" % args.dataset) + print(f"Dataset not supported: {args.dataset}") diff --git a/lib/datasets/cityscapes/tools/convert_coco_model_to_cityscapes.py b/lib/datasets/cityscapes/tools/convert_coco_model_to_cityscapes.py index e1530a868..a5f854f1f 100644 --- a/lib/datasets/cityscapes/tools/convert_coco_model_to_cityscapes.py +++ b/lib/datasets/cityscapes/tools/convert_coco_model_to_cityscapes.py @@ -40,22 +40,18 @@ def parse_args(): parser.print_help() sys.exit(1) - args = parser.parse_args() - return args + return parser.parse_args() def convert_coco_blobs_to_cityscape_blobs(model_dict): for k, v in model_dict['blobs'].items(): - if v.shape[0] == NUM_COCO_CLS or v.shape[0] == 4 * NUM_COCO_CLS: + if v.shape[0] in [NUM_COCO_CLS, 4 * NUM_COCO_CLS]: coco_blob = model_dict['blobs'][k] - print( - 'Converting COCO blob {} with shape {}'. - format(k, coco_blob.shape) - ) + print(f'Converting COCO blob {k} with shape {coco_blob.shape}') cs_blob = convert_coco_blob_to_cityscapes_blob( coco_blob, args.convert_func ) - print(' -> converted shape {}'.format(cs_blob.shape)) + print(f' -> converted shape {cs_blob.shape}') model_dict['blobs'][k] = cs_blob @@ -64,7 +60,7 @@ def convert_coco_blob_to_cityscapes_blob(coco_blob, convert_func): coco_shape = coco_blob.shape leading_factor = int(coco_shape[0] / NUM_COCO_CLS) tail_shape = list(coco_shape[1:]) - assert leading_factor == 1 or leading_factor == 4 + assert leading_factor in {1, 4} # Reshape in [num_classes, ...] form for easier manipulations coco_blob = coco_blob.reshape([NUM_COCO_CLS, -1] + tail_shape) @@ -108,5 +104,5 @@ def load_and_convert_coco_model(args): with open(args.out_file_name, 'w') as f: pickle.dump(weights, f, protocol=pickle.HIGHEST_PROTOCOL) - print('Wrote blobs to {}:'.format(args.out_file_name)) + print(f'Wrote blobs to {args.out_file_name}:') print(sorted(weights['blobs'].keys())) diff --git a/lib/datasets/dataset_catalog.py b/lib/datasets/dataset_catalog.py index 9a0f35dbb..643ad5539 100644 --- a/lib/datasets/dataset_catalog.py +++ b/lib/datasets/dataset_catalog.py @@ -15,6 +15,7 @@ """Collection of available datasets.""" + from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -38,152 +39,99 @@ # Available datasets DATASETS = { 'cityscapes_fine_instanceonly_seg_train': { - IM_DIR: - _DATA_DIR + '/cityscapes/images', - ANN_FN: - _DATA_DIR + '/cityscapes/annotations/instancesonly_gtFine_train.json', - RAW_DIR: - _DATA_DIR + '/cityscapes/raw' + IM_DIR: f'{_DATA_DIR}/cityscapes/images', + ANN_FN: f'{_DATA_DIR}/cityscapes/annotations/instancesonly_gtFine_train.json', + RAW_DIR: f'{_DATA_DIR}/cityscapes/raw', }, 'cityscapes_fine_instanceonly_seg_val': { - IM_DIR: - _DATA_DIR + '/cityscapes/images', - # use filtered validation as there is an issue converting contours - ANN_FN: - _DATA_DIR + '/cityscapes/annotations/instancesonly_filtered_gtFine_val.json', - RAW_DIR: - _DATA_DIR + '/cityscapes/raw' + IM_DIR: f'{_DATA_DIR}/cityscapes/images', + ANN_FN: f'{_DATA_DIR}/cityscapes/annotations/instancesonly_filtered_gtFine_val.json', + RAW_DIR: f'{_DATA_DIR}/cityscapes/raw', }, 'cityscapes_fine_instanceonly_seg_test': { - IM_DIR: - _DATA_DIR + '/cityscapes/images', - ANN_FN: - _DATA_DIR + '/cityscapes/annotations/instancesonly_gtFine_test.json', - RAW_DIR: - _DATA_DIR + '/cityscapes/raw' + IM_DIR: f'{_DATA_DIR}/cityscapes/images', + ANN_FN: f'{_DATA_DIR}/cityscapes/annotations/instancesonly_gtFine_test.json', + RAW_DIR: f'{_DATA_DIR}/cityscapes/raw', }, 'coco_2014_train': { - IM_DIR: - _DATA_DIR + '/coco/coco_train2014', - ANN_FN: - _DATA_DIR + '/coco/annotations/instances_train2014.json' + IM_DIR: f'{_DATA_DIR}/coco/coco_train2014', + ANN_FN: f'{_DATA_DIR}/coco/annotations/instances_train2014.json', }, 'coco_2014_val': { - IM_DIR: - _DATA_DIR + '/coco/coco_val2014', - ANN_FN: - _DATA_DIR + '/coco/annotations/instances_val2014.json' + IM_DIR: f'{_DATA_DIR}/coco/coco_val2014', + ANN_FN: f'{_DATA_DIR}/coco/annotations/instances_val2014.json', }, 'coco_2014_minival': { - IM_DIR: - _DATA_DIR + '/coco/coco_val2014', - ANN_FN: - _DATA_DIR + '/coco/annotations/instances_minival2014.json' + IM_DIR: f'{_DATA_DIR}/coco/coco_val2014', + ANN_FN: f'{_DATA_DIR}/coco/annotations/instances_minival2014.json', }, 'coco_2014_valminusminival': { - IM_DIR: - _DATA_DIR + '/coco/coco_val2014', - ANN_FN: - _DATA_DIR + '/coco/annotations/instances_valminusminival2014.json' + IM_DIR: f'{_DATA_DIR}/coco/coco_val2014', + ANN_FN: f'{_DATA_DIR}/coco/annotations/instances_valminusminival2014.json', }, 'coco_2015_test': { - IM_DIR: - _DATA_DIR + '/coco/coco_test2015', - ANN_FN: - _DATA_DIR + '/coco/annotations/image_info_test2015.json' + IM_DIR: f'{_DATA_DIR}/coco/coco_test2015', + ANN_FN: f'{_DATA_DIR}/coco/annotations/image_info_test2015.json', }, 'coco_2015_test-dev': { - IM_DIR: - _DATA_DIR + '/coco/coco_test2015', - ANN_FN: - _DATA_DIR + '/coco/annotations/image_info_test-dev2015.json' - }, - 'coco_2017_test': { # 2017 test uses 2015 test images - IM_DIR: - _DATA_DIR + '/coco/coco_test2015', - ANN_FN: - _DATA_DIR + '/coco/annotations/image_info_test2017.json', - IM_PREFIX: - 'COCO_test2015_' - }, - 'coco_2017_test-dev': { # 2017 test-dev uses 2015 test images - IM_DIR: - _DATA_DIR + '/coco/coco_test2015', - ANN_FN: - _DATA_DIR + '/coco/annotations/image_info_test-dev2017.json', - IM_PREFIX: - 'COCO_test2015_' + IM_DIR: f'{_DATA_DIR}/coco/coco_test2015', + ANN_FN: f'{_DATA_DIR}/coco/annotations/image_info_test-dev2015.json', + }, + 'coco_2017_test': { + IM_DIR: f'{_DATA_DIR}/coco/coco_test2015', + ANN_FN: f'{_DATA_DIR}/coco/annotations/image_info_test2017.json', + IM_PREFIX: 'COCO_test2015_', + }, + 'coco_2017_test-dev': { + IM_DIR: f'{_DATA_DIR}/coco/coco_test2015', + ANN_FN: f'{_DATA_DIR}/coco/annotations/image_info_test-dev2017.json', + IM_PREFIX: 'COCO_test2015_', }, 'coco_stuff_train': { - IM_DIR: - _DATA_DIR + '/coco/coco_train2014', - ANN_FN: - _DATA_DIR + '/coco/annotations/coco_stuff_train.json' + IM_DIR: f'{_DATA_DIR}/coco/coco_train2014', + ANN_FN: f'{_DATA_DIR}/coco/annotations/coco_stuff_train.json', }, 'coco_stuff_val': { - IM_DIR: - _DATA_DIR + '/coco/coco_val2014', - ANN_FN: - _DATA_DIR + '/coco/annotations/coco_stuff_val.json' + IM_DIR: f'{_DATA_DIR}/coco/coco_val2014', + ANN_FN: f'{_DATA_DIR}/coco/annotations/coco_stuff_val.json', }, 'keypoints_coco_2014_train': { - IM_DIR: - _DATA_DIR + '/coco/coco_train2014', - ANN_FN: - _DATA_DIR + '/coco/annotations/person_keypoints_train2014.json' + IM_DIR: f'{_DATA_DIR}/coco/coco_train2014', + ANN_FN: f'{_DATA_DIR}/coco/annotations/person_keypoints_train2014.json', }, 'keypoints_coco_2014_val': { - IM_DIR: - _DATA_DIR + '/coco/coco_val2014', - ANN_FN: - _DATA_DIR + '/coco/annotations/person_keypoints_val2014.json' + IM_DIR: f'{_DATA_DIR}/coco/coco_val2014', + ANN_FN: f'{_DATA_DIR}/coco/annotations/person_keypoints_val2014.json', }, 'keypoints_coco_2014_minival': { - IM_DIR: - _DATA_DIR + '/coco/coco_val2014', - ANN_FN: - _DATA_DIR + '/coco/annotations/person_keypoints_minival2014.json' + IM_DIR: f'{_DATA_DIR}/coco/coco_val2014', + ANN_FN: f'{_DATA_DIR}/coco/annotations/person_keypoints_minival2014.json', }, 'keypoints_coco_2014_valminusminival': { - IM_DIR: - _DATA_DIR + '/coco/coco_val2014', - ANN_FN: - _DATA_DIR + '/coco/annotations/person_keypoints_valminusminival2014.json' + IM_DIR: f'{_DATA_DIR}/coco/coco_val2014', + ANN_FN: f'{_DATA_DIR}/coco/annotations/person_keypoints_valminusminival2014.json', }, 'keypoints_coco_2015_test': { - IM_DIR: - _DATA_DIR + '/coco/coco_test2015', - ANN_FN: - _DATA_DIR + '/coco/annotations/image_info_test2015.json' + IM_DIR: f'{_DATA_DIR}/coco/coco_test2015', + ANN_FN: f'{_DATA_DIR}/coco/annotations/image_info_test2015.json', }, 'keypoints_coco_2015_test-dev': { - IM_DIR: - _DATA_DIR + '/coco/coco_test2015', - ANN_FN: - _DATA_DIR + '/coco/annotations/image_info_test-dev2015.json' + IM_DIR: f'{_DATA_DIR}/coco/coco_test2015', + ANN_FN: f'{_DATA_DIR}/coco/annotations/image_info_test-dev2015.json', }, 'voc_2007_trainval': { - IM_DIR: - _DATA_DIR + '/VOC2007/JPEGImages', - ANN_FN: - _DATA_DIR + '/VOC2007/annotations/voc_2007_trainval.json', - DEVKIT_DIR: - _DATA_DIR + '/VOC2007/VOCdevkit2007' + IM_DIR: f'{_DATA_DIR}/VOC2007/JPEGImages', + ANN_FN: f'{_DATA_DIR}/VOC2007/annotations/voc_2007_trainval.json', + DEVKIT_DIR: f'{_DATA_DIR}/VOC2007/VOCdevkit2007', }, 'voc_2007_test': { - IM_DIR: - _DATA_DIR + '/VOC2007/JPEGImages', - ANN_FN: - _DATA_DIR + '/VOC2007/annotations/voc_2007_test.json', - DEVKIT_DIR: - _DATA_DIR + '/VOC2007/VOCdevkit2007' + IM_DIR: f'{_DATA_DIR}/VOC2007/JPEGImages', + ANN_FN: f'{_DATA_DIR}/VOC2007/annotations/voc_2007_test.json', + DEVKIT_DIR: f'{_DATA_DIR}/VOC2007/VOCdevkit2007', }, 'voc_2012_trainval': { - IM_DIR: - _DATA_DIR + '/VOC2012/JPEGImages', - ANN_FN: - _DATA_DIR + '/VOC2012/annotations/voc_2012_trainval.json', - DEVKIT_DIR: - _DATA_DIR + '/VOC2012/VOCdevkit2012' - } + IM_DIR: f'{_DATA_DIR}/VOC2012/JPEGImages', + ANN_FN: f'{_DATA_DIR}/VOC2012/annotations/voc_2012_trainval.json', + DEVKIT_DIR: f'{_DATA_DIR}/VOC2012/VOCdevkit2012', + }, } diff --git a/lib/datasets/dummy_datasets.py b/lib/datasets/dummy_datasets.py index 5d7e6cfa7..69133ba42 100644 --- a/lib/datasets/dummy_datasets.py +++ b/lib/datasets/dummy_datasets.py @@ -43,5 +43,5 @@ def get_coco_dataset(): 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', 'hair drier', 'toothbrush' ] - ds.classes = {i: name for i, name in enumerate(classes)} + ds.classes = dict(enumerate(classes)) return ds diff --git a/lib/datasets/json_dataset.py b/lib/datasets/json_dataset.py index cece62127..7895e9ff4 100644 --- a/lib/datasets/json_dataset.py +++ b/lib/datasets/json_dataset.py @@ -54,13 +54,16 @@ class JsonDataset(object): """A class representing a COCO json dataset.""" def __init__(self, name): - assert name in DATASETS.keys(), \ - 'Unknown dataset name: {}'.format(name) - assert os.path.exists(DATASETS[name][IM_DIR]), \ - 'Image directory \'{}\' not found'.format(DATASETS[name][IM_DIR]) - assert os.path.exists(DATASETS[name][ANN_FN]), \ - 'Annotation file \'{}\' not found'.format(DATASETS[name][ANN_FN]) - logger.debug('Creating: {}'.format(name)) + assert name in DATASETS.keys(), f'Unknown dataset name: {name}' + assert os.path.exists( + DATASETS[name][IM_DIR] + ), f"Image directory '{DATASETS[name][IM_DIR]}' not found" + + assert os.path.exists( + DATASETS[name][ANN_FN] + ), f"Annotation file '{DATASETS[name][ANN_FN]}' not found" + + logger.debug(f'Creating: {name}') self.name = name self.image_directory = DATASETS[name][IM_DIR] self.image_prefix = ( @@ -252,7 +255,7 @@ def _add_proposals_from_file( self, roidb, proposal_file, min_proposal_size, top_k, crowd_thresh ): """Add proposals from a proposals file to an roidb.""" - logger.info('Loading proposals from: {}'.format(proposal_file)) + logger.info(f'Loading proposals from: {proposal_file}') with open(proposal_file, 'r') as f: proposals = pickle.load(f) id_field = 'indexes' if 'indexes' in proposals else 'ids' # compat fix @@ -313,7 +316,7 @@ def _get_gt_keypoints(self, obj): if 'keypoints' not in obj: return None kp = np.array(obj['keypoints']) - x = kp[0::3] # 0-indexed x coordinates + x = kp[::3] y = kp[1::3] # 0-indexed y coordinates # 0: not labeled; 1: labeled, not inside mask; # 2: labeled and inside mask diff --git a/lib/datasets/json_dataset_evaluator.py b/lib/datasets/json_dataset_evaluator.py index ba67517b7..ca67280fc 100644 --- a/lib/datasets/json_dataset_evaluator.py +++ b/lib/datasets/json_dataset_evaluator.py @@ -44,10 +44,11 @@ def evaluate_masks( cleanup=False ): res_file = os.path.join( - output_dir, 'segmentations_' + json_dataset.name + '_results' + output_dir, f'segmentations_{json_dataset.name}_results' ) + if use_salt: - res_file += '_{}'.format(str(uuid.uuid4())) + res_file += f'_{str(uuid.uuid4())}' res_file += '.json' _write_coco_segms_results_file( json_dataset, all_boxes, all_segms, res_file) @@ -79,8 +80,9 @@ def _write_coco_segms_results_file( results.extend(_coco_segms_results_one_category( json_dataset, all_boxes[cls_ind], all_segms[cls_ind], cat_id)) logger.info( - 'Writing segmentation results json to: {}'.format( - os.path.abspath(res_file))) + f'Writing segmentation results json to: {os.path.abspath(res_file)}' + ) + with open(res_file, 'w') as fid: json.dump(results, fid) @@ -119,18 +121,16 @@ def _do_segmentation_eval(json_dataset, res_file, output_dir): _log_detection_eval_metrics(json_dataset, coco_eval) eval_file = os.path.join(output_dir, 'segmentation_results.pkl') save_object(coco_eval, eval_file) - logger.info('Wrote json eval results to: {}'.format(eval_file)) + logger.info(f'Wrote json eval results to: {eval_file}') return coco_eval def evaluate_boxes( json_dataset, all_boxes, output_dir, use_salt=True, cleanup=False ): - res_file = os.path.join( - output_dir, 'bbox_' + json_dataset.name + '_results' - ) + res_file = os.path.join(output_dir, f'bbox_{json_dataset.name}_results') if use_salt: - res_file += '_{}'.format(str(uuid.uuid4())) + res_file += f'_{str(uuid.uuid4())}' res_file += '.json' _write_coco_bbox_results_file(json_dataset, all_boxes, res_file) # Only do evaluation on non-test sets (annotations are undisclosed on test) @@ -158,8 +158,7 @@ def _write_coco_bbox_results_file(json_dataset, all_boxes, res_file): cat_id = json_dataset.category_to_id_map[cls] results.extend(_coco_bbox_results_one_category( json_dataset, all_boxes[cls_ind], cat_id)) - logger.info( - 'Writing bbox results json to: {}'.format(os.path.abspath(res_file))) + logger.info(f'Writing bbox results json to: {os.path.abspath(res_file)}') with open(res_file, 'w') as fid: json.dump(results, fid) @@ -196,7 +195,7 @@ def _do_detection_eval(json_dataset, res_file, output_dir): _log_detection_eval_metrics(json_dataset, coco_eval) eval_file = os.path.join(output_dir, 'detection_results.pkl') save_object(coco_eval, eval_file) - logger.info('Wrote json eval results to: {}'.format(eval_file)) + logger.info(f'Wrote json eval results to: {eval_file}') return coco_eval @@ -260,7 +259,7 @@ def evaluate_box_proposals( [128**2, 256**2], # 128-256 [256**2, 512**2], # 256-512 [512**2, 1e5**2]] # 512-inf - assert area in areas, 'Unknown area range: {}'.format(area) + assert area in areas, f'Unknown area range: {area}' area_range = area_ranges[areas[area]] gt_overlaps = np.zeros(0) num_pos = 0 @@ -325,11 +324,9 @@ def evaluate_keypoints( use_salt=True, cleanup=False ): - res_file = os.path.join( - output_dir, 'keypoints_' + json_dataset.name + '_results' - ) + res_file = os.path.join(output_dir, f'keypoints_{json_dataset.name}_results') if use_salt: - res_file += '_{}'.format(str(uuid.uuid4())) + res_file += f'_{str(uuid.uuid4())}' res_file += '.json' _write_coco_keypoint_results_file( json_dataset, all_boxes, all_keypoints, res_file) @@ -359,9 +356,7 @@ def _write_coco_keypoint_results_file( cat_id = json_dataset.category_to_id_map[cls] results.extend(_coco_kp_results_one_category( json_dataset, all_boxes[cls_ind], all_keypoints[cls_ind], cat_id)) - logger.info( - 'Writing keypoint results json to: {}'.format( - os.path.abspath(res_file))) + logger.info(f'Writing keypoint results json to: {os.path.abspath(res_file)}') with open(res_file, 'w') as fid: json.dump(results, fid) @@ -373,14 +368,14 @@ def _coco_kp_results_one_category(json_dataset, boxes, kps, cat_id): assert len(kps) == len(image_ids) assert len(boxes) == len(image_ids) use_box_score = False - if cfg.KRCNN.KEYPOINT_CONFIDENCE == 'logit': + if cfg.KRCNN.KEYPOINT_CONFIDENCE == 'bbox': + use_box_score = True + elif cfg.KRCNN.KEYPOINT_CONFIDENCE == 'logit': # This is ugly; see utils.keypoints.heatmap_to_keypoints for the magic # indexes score_index = 2 elif cfg.KRCNN.KEYPOINT_CONFIDENCE == 'prob': score_index = 3 - elif cfg.KRCNN.KEYPOINT_CONFIDENCE == 'bbox': - use_box_score = True else: raise ValueError( 'KRCNN.KEYPOINT_CONFIDENCE must be "logit", "prob", or "bbox"') @@ -396,9 +391,7 @@ def _coco_kp_results_one_category(json_dataset, boxes, kps, cat_id): kps_score = 0 for k in range(kps_dets[j].shape[1]): - xy.append(float(kps_dets[j][0, k])) - xy.append(float(kps_dets[j][1, k])) - xy.append(1) + xy.extend((float(kps_dets[j][0, k]), float(kps_dets[j][1, k]), 1)) if not use_box_score: kps_score += kps_dets[j][score_index, k] @@ -425,6 +418,6 @@ def _do_keypoint_eval(json_dataset, res_file, output_dir): coco_eval.accumulate() eval_file = os.path.join(output_dir, 'keypoint_results.pkl') save_object(coco_eval, eval_file) - logger.info('Wrote json eval results to: {}'.format(eval_file)) + logger.info(f'Wrote json eval results to: {eval_file}') coco_eval.summarize() return coco_eval diff --git a/lib/datasets/roidb.py b/lib/datasets/roidb.py index 4ffc03a5b..e3a83212d 100644 --- a/lib/datasets/roidb.py +++ b/lib/datasets/roidb.py @@ -81,6 +81,7 @@ def extend_with_flipped_entries(roidb, dataset): ground truth boxes and object proposals) are horizontally flipped. """ flipped_roidb = [] + dont_copy = ('boxes', 'segms', 'gt_keypoints', 'flipped') for entry in roidb: width = entry['width'] boxes = entry['boxes'].copy() @@ -89,11 +90,7 @@ def extend_with_flipped_entries(roidb, dataset): boxes[:, 0] = width - oldx2 - 1 boxes[:, 2] = width - oldx1 - 1 assert (boxes[:, 2] >= boxes[:, 0]).all() - flipped_entry = {} - dont_copy = ('boxes', 'segms', 'gt_keypoints', 'flipped') - for k, v in entry.items(): - if k not in dont_copy: - flipped_entry[k] = v + flipped_entry = {k: v for k, v in entry.items() if k not in dont_copy} flipped_entry['boxes'] = boxes flipped_entry['segms'] = segm_utils.flip_segms( entry['segms'], entry['height'], entry['width'] diff --git a/lib/datasets/task_evaluation.py b/lib/datasets/task_evaluation.py index 0d8f4496a..f8b7a8037 100644 --- a/lib/datasets/task_evaluation.py +++ b/lib/datasets/task_evaluation.py @@ -93,9 +93,7 @@ def evaluate_boxes(dataset, all_boxes, output_dir, use_matlab=False): ) box_results = _voc_eval_to_box_results(voc_eval) else: - raise NotImplementedError( - 'No evaluator for dataset: {}'.format(dataset.name) - ) + raise NotImplementedError(f'No evaluator for dataset: {dataset.name}') return OrderedDict([(dataset.name, box_results)]) @@ -124,9 +122,7 @@ def evaluate_masks(dataset, all_boxes, all_segms, output_dir): ) mask_results = _cs_eval_to_mask_results(cs_eval) else: - raise NotImplementedError( - 'No evaluator for dataset: {}'.format(dataset.name) - ) + raise NotImplementedError(f'No evaluator for dataset: {dataset.name}') return OrderedDict([(dataset.name, mask_results)]) @@ -166,7 +162,7 @@ def log_box_proposal_results(results): """Log bounding box proposal results.""" for dataset in results.keys(): keys = results[dataset]['box_proposal'].keys() - pad = max([len(k) for k in keys]) + pad = max(len(k) for k in keys) logger.info(dataset) for k, v in results[dataset]['box_proposal'].items(): logger.info('{}: {:.3f}'.format(k.ljust(pad), v)) @@ -177,9 +173,9 @@ def log_copy_paste_friendly_results(results): spreadsheet. Lines are prefixed with 'copypaste: ' to make grepping easy. """ for dataset in results.keys(): - logger.info('copypaste: Dataset: {}'.format(dataset)) + logger.info(f'copypaste: Dataset: {dataset}') for task, metrics in results[dataset].items(): - logger.info('copypaste: Task: {}'.format(task)) + logger.info(f'copypaste: Task: {task}') metric_names = metrics.keys() metric_vals = ['{:.4f}'.format(v) for v in metrics.values()] logger.info('copypaste: ' + ','.join(metric_names)) @@ -200,10 +196,9 @@ def check_expected_results(results, atol=0.005, rtol=0.1): return for dataset, task, metric, expected_val in cfg.EXPECTED_RESULTS: - assert dataset in results, 'Dataset {} not in results'.format(dataset) - assert task in results[dataset], 'Task {} not in results'.format(task) - assert metric in results[dataset][task], \ - 'Metric {} not in results'.format(metric) + assert dataset in results, f'Dataset {dataset} not in results' + assert task in results[dataset], f'Task {task} not in results' + assert metric in results[dataset][task], f'Metric {metric} not in results' actual_val = results[dataset][task][metric] err = abs(actual_val - expected_val) tol = atol + rtol * abs(expected_val) @@ -212,7 +207,7 @@ def check_expected_results(results, atol=0.005, rtol=0.1): '{:.3f} vs. {:.3f}, err={:.3f}, tol={:.3f}' ).format(dataset, task, metric, actual_val, expected_val, err, tol) if err > tol: - msg = 'FAIL: ' + msg + msg = f'FAIL: {msg}' logger.error(msg) if cfg.EXPECTED_RESULTS_EMAIL != '': subject = 'Detectron end-to-end test failure' @@ -238,7 +233,7 @@ def check_expected_results(results, atol=0.005, rtol=0.1): subject, '\n\n'.join(body), cfg.EXPECTED_RESULTS_EMAIL ) else: - msg = 'PASS: ' + msg + msg = f'PASS: {msg}' logger.info(msg) diff --git a/lib/datasets/voc_dataset_evaluator.py b/lib/datasets/voc_dataset_evaluator.py index 2eae2a1f4..cac7dff65 100644 --- a/lib/datasets/voc_dataset_evaluator.py +++ b/lib/datasets/voc_dataset_evaluator.py @@ -43,7 +43,7 @@ def evaluate_boxes( cleanup=True, use_matlab=False ): - salt = '_{}'.format(str(uuid.uuid4())) if use_salt else '' + salt = f'_{str(uuid.uuid4())}' if use_salt else '' filenames = _write_voc_results_files(json_dataset, all_boxes, salt) _do_python_eval(json_dataset, salt, output_dir) if use_matlab: @@ -58,8 +58,10 @@ def evaluate_boxes( def _write_voc_results_files(json_dataset, all_boxes, salt): filenames = [] image_set_path = voc_info(json_dataset)['image_set_path'] - assert os.path.exists(image_set_path), \ - 'Image set path does not exist: {}'.format(image_set_path) + assert os.path.exists( + image_set_path + ), f'Image set path does not exist: {image_set_path}' + with open(image_set_path, 'r') as f: image_index = [x.strip() for x in f.readlines()] # Sanity check that order of images in json dataset matches order in the @@ -71,7 +73,7 @@ def _write_voc_results_files(json_dataset, all_boxes, salt): for cls_ind, cls in enumerate(json_dataset.classes): if cls == '__background__': continue - logger.info('Writing VOC results for: {}'.format(cls)) + logger.info(f'Writing VOC results for: {cls}') filename = _get_voc_results_file_template(json_dataset, salt).format(cls) filenames.append(filename) @@ -98,8 +100,8 @@ def _get_voc_results_file_template(json_dataset, salt): image_set = info['image_set'] devkit_path = info['devkit_path'] # VOCdevkit/results/VOC2007/Main/_det_test_aeroplane.txt - filename = 'comp4' + salt + '_det_' + image_set + '_{:s}.txt' - return os.path.join(devkit_path, 'results', 'VOC' + year, 'Main', filename) + filename = f'comp4{salt}_det_{image_set}' + '_{:s}.txt' + return os.path.join(devkit_path, 'results', f'VOC{year}', 'Main', filename) def _do_python_eval(json_dataset, salt, output_dir='output'): @@ -111,11 +113,11 @@ def _do_python_eval(json_dataset, salt, output_dir='output'): cachedir = os.path.join(devkit_path, 'annotations_cache') aps = [] # The PASCAL VOC metric changed in 2010 - use_07_metric = True if int(year) < 2010 else False + use_07_metric = int(year) < 2010 logger.info('VOC07 metric? ' + ('Yes' if use_07_metric else 'No')) if not os.path.isdir(output_dir): os.mkdir(output_dir) - for _, cls in enumerate(json_dataset.classes): + for cls in json_dataset.classes: if cls == '__background__': continue filename = _get_voc_results_file_template( @@ -125,7 +127,7 @@ def _do_python_eval(json_dataset, salt, output_dir='output'): use_07_metric=use_07_metric) aps += [ap] logger.info('AP for {} = {:.4f}'.format(cls, ap)) - res_file = os.path.join(output_dir, cls + '_pr.pkl') + res_file = os.path.join(output_dir, f'{cls}_pr.pkl') save_object({'rec': rec, 'prec': prec, 'ap': ap}, res_file) logger.info('Mean AP = {:.4f}'.format(np.mean(aps))) logger.info('~~~~~~~~') @@ -151,12 +153,12 @@ def _do_matlab_eval(json_dataset, salt, output_dir='output'): info = voc_info(json_dataset) path = os.path.join( cfg.ROOT_DIR, 'lib', 'datasets', 'VOCdevkit-matlab-wrapper') - cmd = 'cd {} && '.format(path) - cmd += '{:s} -nodisplay -nodesktop '.format(cfg.MATLAB) + cmd = f'cd {path} && ' + '{:s} -nodisplay -nodesktop '.format(cfg.MATLAB) cmd += '-r "dbstop if error; ' - cmd += 'voc_eval(\'{:s}\',\'{:s}\',\'{:s}\',\'{:s}\'); quit;"' \ - .format(info['devkit_path'], 'comp4' + salt, info['image_set'], - output_dir) + cmd += 'voc_eval(\'{:s}\',\'{:s}\',\'{:s}\',\'{:s}\'); quit;"'.format( + info['devkit_path'], f'comp4{salt}', info['image_set'], output_dir + ) + logger.info('Running:\n{}'.format(cmd)) subprocess.call(cmd, shell=True) @@ -165,12 +167,12 @@ def voc_info(json_dataset): year = json_dataset.name[4:8] image_set = json_dataset.name[9:] devkit_path = DATASETS[json_dataset.name][DEVKIT_DIR] - assert os.path.exists(devkit_path), \ - 'Devkit directory {} not found'.format(devkit_path) - anno_path = os.path.join( - devkit_path, 'VOC' + year, 'Annotations', '{:s}.xml') + assert os.path.exists(devkit_path), f'Devkit directory {devkit_path} not found' + anno_path = os.path.join(devkit_path, f'VOC{year}', 'Annotations', '{:s}.xml') image_set_path = os.path.join( - devkit_path, 'VOC' + year, 'ImageSets', 'Main', image_set + '.txt') + devkit_path, f'VOC{year}', 'ImageSets', 'Main', f'{image_set}.txt' + ) + return dict( year=year, image_set=image_set, diff --git a/lib/datasets/voc_eval.py b/lib/datasets/voc_eval.py index d972ba3e3..c521e13b5 100644 --- a/lib/datasets/voc_eval.py +++ b/lib/datasets/voc_eval.py @@ -36,8 +36,7 @@ def parse_rec(filename): tree = ET.parse(filename) objects = [] for obj in tree.findall('object'): - obj_struct = {} - obj_struct['name'] = obj.find('name').text + obj_struct = {'name': obj.find('name').text} obj_struct['pose'] = obj.find('pose').text obj_struct['truncated'] = int(obj.find('truncated').text) obj_struct['difficult'] = int(obj.find('difficult').text) @@ -59,10 +58,7 @@ def voc_ap(rec, prec, use_07_metric=False): # 11 point metric ap = 0. for t in np.arange(0., 1.1, 0.1): - if np.sum(rec >= t) == 0: - p = 0 - else: - p = np.max(prec[rec >= t]) + p = 0 if np.sum(rec >= t) == 0 else np.max(prec[rec >= t]) ap = ap + p / 11. else: # correct AP calculation @@ -119,7 +115,7 @@ def voc_eval(detpath, if not os.path.isdir(cachedir): os.mkdir(cachedir) imageset = os.path.splitext(os.path.basename(imagesetfile))[0] - cachefile = os.path.join(cachedir, imageset + '_annots.pkl') + cachefile = os.path.join(cachedir, f'{imageset}_annots.pkl') # read list of images with open(imagesetfile, 'r') as f: lines = f.readlines() @@ -201,16 +197,15 @@ def voc_eval(detpath, ovmax = np.max(overlaps) jmax = np.argmax(overlaps) - if ovmax > ovthresh: - if not R['difficult'][jmax]: - if not R['det'][jmax]: - tp[d] = 1. - R['det'][jmax] = 1 - else: - fp[d] = 1. - else: + if ( + ovmax > ovthresh + and not R['difficult'][jmax] + and not R['det'][jmax] + ): + tp[d] = 1. + R['det'][jmax] = 1 + elif ovmax > ovthresh and not R['difficult'][jmax] or ovmax <= ovthresh: fp[d] = 1. - # compute precision recall fp = np.cumsum(fp) tp = np.cumsum(tp) diff --git a/lib/modeling/FPN.py b/lib/modeling/FPN.py index 6188bfdf0..8106dce17 100644 --- a/lib/modeling/FPN.py +++ b/lib/modeling/FPN.py @@ -132,9 +132,9 @@ def add_fpn(model, fpn_level_info): lateral_input_blobs = fpn_level_info.blobs[:num_backbone_stages] output_blobs = [ - 'fpn_inner_{}'.format(s) - for s in fpn_level_info.blobs[:num_backbone_stages] + f'fpn_inner_{s}' for s in fpn_level_info.blobs[:num_backbone_stages] ] + fpn_dim_lateral = fpn_level_info.dims xavier_fill = ('XavierFill', {}) @@ -172,15 +172,16 @@ def add_fpn(model, fpn_level_info): for i in range(num_backbone_stages): fpn_blob = model.Conv( output_blobs[i], - 'fpn_{}'.format(fpn_level_info.blobs[i]), + f'fpn_{fpn_level_info.blobs[i]}', dim_in=fpn_dim, dim_out=fpn_dim, kernel=3, pad=1, stride=1, weight_init=xavier_fill, - bias_init=const_fill(0.0) + bias_init=const_fill(0.0), ) + blobs_fpn += [fpn_blob] spatial_scales += [fpn_level_info.spatial_scales[i]] @@ -192,7 +193,7 @@ def add_fpn(model, fpn_level_info): if not cfg.FPN.EXTRA_CONV_LEVELS and max_level == HIGHEST_BACKBONE_LVL + 1: # Original FPN P6 level implementation from our CVPR'17 FPN paper P6_blob_in = blobs_fpn[0] - P6_name = P6_blob_in + '_subsampled_2x' + P6_name = f'{P6_blob_in}_subsampled_2x' # Use max pooling to simulate stride 2 subsampling P6_blob = model.MaxPool(P6_blob_in, P6_name, kernel=1, pad=0, stride=2) blobs_fpn.insert(0, P6_blob) @@ -205,18 +206,19 @@ def add_fpn(model, fpn_level_info): for i in range(HIGHEST_BACKBONE_LVL + 1, max_level + 1): fpn_blob_in = fpn_blob if i > HIGHEST_BACKBONE_LVL + 1: - fpn_blob_in = model.Relu(fpn_blob, fpn_blob + '_relu') + fpn_blob_in = model.Relu(fpn_blob, f'{fpn_blob}_relu') fpn_blob = model.Conv( fpn_blob_in, - 'fpn_' + str(i), + f'fpn_{str(i)}', dim_in=dim_in, dim_out=fpn_dim, kernel=3, pad=1, stride=2, weight_init=xavier_fill, - bias_init=const_fill(0.0) + bias_init=const_fill(0.0), ) + dim_in = fpn_dim blobs_fpn.insert(0, fpn_blob) spatial_scales.insert(0, spatial_scales[0] * 0.5) @@ -231,20 +233,22 @@ def add_topdown_lateral_module( # Lateral 1x1 conv lat = model.Conv( fpn_lateral, - fpn_bottom + '_lateral', + f'{fpn_bottom}_lateral', dim_in=dim_lateral, dim_out=dim_top, kernel=1, pad=0, stride=1, weight_init=( - const_fill(0.0) if cfg.FPN.ZERO_INIT_LATERAL + const_fill(0.0) + if cfg.FPN.ZERO_INIT_LATERAL else ('XavierFill', {}) ), - bias_init=const_fill(0.0) + bias_init=const_fill(0.0), ) + # Top-down 2x upsampling - td = model.net.UpsampleNearest(fpn_top, fpn_bottom + '_topdown', scale=2) + td = model.net.UpsampleNearest(fpn_top, f'{fpn_bottom}_topdown', scale=2) # Sum lateral and top-down model.net.Sum([lat, td], fpn_bottom) @@ -291,81 +295,87 @@ def add_fpn_rpn_outputs(model, blobs_in, dim_in, spatial_scales): # RPN hidden representation conv_rpn_fpn = model.Conv( bl_in, - 'conv_rpn_fpn' + slvl, + f'conv_rpn_fpn{slvl}', dim_in, dim_out, kernel=3, pad=1, stride=1, weight_init=gauss_fill(0.01), - bias_init=const_fill(0.0) + bias_init=const_fill(0.0), ) + model.Relu(conv_rpn_fpn, conv_rpn_fpn) # Proposal classification scores rpn_cls_logits_fpn = model.Conv( conv_rpn_fpn, - 'rpn_cls_logits_fpn' + slvl, + f'rpn_cls_logits_fpn{slvl}', dim_in, num_anchors, kernel=1, pad=0, stride=1, weight_init=gauss_fill(0.01), - bias_init=const_fill(0.0) + bias_init=const_fill(0.0), ) + # Proposal bbox regression deltas rpn_bbox_pred_fpn = model.Conv( conv_rpn_fpn, - 'rpn_bbox_pred_fpn' + slvl, + f'rpn_bbox_pred_fpn{slvl}', dim_in, 4 * num_anchors, kernel=1, pad=0, stride=1, weight_init=gauss_fill(0.01), - bias_init=const_fill(0.0) + bias_init=const_fill(0.0), ) + else: # Share weights and biases sk_min = str(k_min) # RPN hidden representation conv_rpn_fpn = model.ConvShared( bl_in, - 'conv_rpn_fpn' + slvl, + f'conv_rpn_fpn{slvl}', dim_in, dim_out, kernel=3, pad=1, stride=1, - weight='conv_rpn_fpn' + sk_min + '_w', - bias='conv_rpn_fpn' + sk_min + '_b' + weight=f'conv_rpn_fpn{sk_min}_w', + bias=f'conv_rpn_fpn{sk_min}_b', ) + model.Relu(conv_rpn_fpn, conv_rpn_fpn) # Proposal classification scores rpn_cls_logits_fpn = model.ConvShared( conv_rpn_fpn, - 'rpn_cls_logits_fpn' + slvl, + f'rpn_cls_logits_fpn{slvl}', dim_in, num_anchors, kernel=1, pad=0, stride=1, - weight='rpn_cls_logits_fpn' + sk_min + '_w', - bias='rpn_cls_logits_fpn' + sk_min + '_b' + weight=f'rpn_cls_logits_fpn{sk_min}_w', + bias=f'rpn_cls_logits_fpn{sk_min}_b', ) + # Proposal bbox regression deltas rpn_bbox_pred_fpn = model.ConvShared( conv_rpn_fpn, - 'rpn_bbox_pred_fpn' + slvl, + f'rpn_bbox_pred_fpn{slvl}', dim_in, 4 * num_anchors, kernel=1, pad=0, stride=1, - weight='rpn_bbox_pred_fpn' + sk_min + '_w', - bias='rpn_bbox_pred_fpn' + sk_min + '_b' + weight=f'rpn_bbox_pred_fpn{sk_min}_w', + bias=f'rpn_bbox_pred_fpn{sk_min}_b', ) + if not model.train or cfg.MODEL.FASTER_RCNN: # Proposals are needed during: # 1) inference (== not model.train) for RPN only and Faster R-CNN @@ -378,13 +388,14 @@ def add_fpn_rpn_outputs(model, blobs_in, dim_in, spatial_scales): aspect_ratios=cfg.FPN.RPN_ASPECT_RATIOS ) rpn_cls_probs_fpn = model.net.Sigmoid( - rpn_cls_logits_fpn, 'rpn_cls_probs_fpn' + slvl + rpn_cls_logits_fpn, f'rpn_cls_probs_fpn{slvl}' ) + model.GenerateProposals( [rpn_cls_probs_fpn, rpn_bbox_pred_fpn, 'im_info'], - ['rpn_rois_fpn' + slvl, 'rpn_roi_probs_fpn' + slvl], + [f'rpn_rois_fpn{slvl}', f'rpn_roi_probs_fpn{slvl}'], anchors=lvl_anchors, - spatial_scale=sc + spatial_scale=sc, ) @@ -474,13 +485,11 @@ def add_multilevel_roi_blobs( rois_stacked = np.zeros((0, 5), dtype=np.float32) # for assert for lvl in range(lvl_min, lvl_max + 1): idx_lvl = np.where(target_lvls == lvl)[0] - blobs[blob_prefix + '_fpn' + str(lvl)] = rois[idx_lvl, :] + blobs[f'{blob_prefix}_fpn{str(lvl)}'] = rois[idx_lvl, :] rois_idx_order = np.concatenate((rois_idx_order, idx_lvl)) - rois_stacked = np.vstack( - [rois_stacked, blobs[blob_prefix + '_fpn' + str(lvl)]] - ) + rois_stacked = np.vstack([rois_stacked, blobs[f'{blob_prefix}_fpn{str(lvl)}']]) rois_idx_restore = np.argsort(rois_idx_order).astype(np.int32, copy=False) - blobs[blob_prefix + '_idx_restore_int32'] = rois_idx_restore + blobs[f'{blob_prefix}_idx_restore_int32'] = rois_idx_restore # Sanity check that restore order is correct assert (rois_stacked[rois_idx_restore] == rois).all() diff --git a/lib/modeling/ResNet.py b/lib/modeling/ResNet.py index 64762a191..ac974285a 100644 --- a/lib/modeling/ResNet.py +++ b/lib/modeling/ResNet.py @@ -71,17 +71,16 @@ def add_stage( for i in range(n): blob_in = add_residual_block( model, - '{}_{}'.format(prefix, i), + f'{prefix}_{i}', blob_in, dim_in, dim_out, dim_inner, dilation, stride_init, - # Not using inplace for the last block; - # it may be fetched externally or used by FPN - inplace_sum=i < n - 1 + inplace_sum=i < n - 1, ) + dim_in = dim_out return blob_in, dim_in @@ -186,7 +185,7 @@ def add_residual_block( if inplace_sum: s = model.net.Sum([tr, sc], tr) else: - s = model.net.Sum([tr, sc], prefix + '_sum') + s = model.net.Sum([tr, sc], f'{prefix}_sum') return model.Relu(s, s) @@ -197,14 +196,15 @@ def add_shortcut(model, prefix, blob_in, dim_in, dim_out, stride): c = model.Conv( blob_in, - prefix + '_branch1', + f'{prefix}_branch1', dim_in, dim_out, kernel=1, stride=stride, - no_bias=1 + no_bias=1, ) - return model.AffineChannel(c, prefix + '_branch1_bn', dim=dim_out) + + return model.AffineChannel(c, f'{prefix}_branch1_bn', dim=dim_out) # ------------------------------------------------------------------------------ @@ -231,20 +231,21 @@ def bottleneck_transformation( # conv 1x1 -> BN -> ReLU cur = model.ConvAffine( blob_in, - prefix + '_branch2a', + f'{prefix}_branch2a', dim_in, dim_inner, kernel=1, stride=str1x1, pad=0, - inplace=True + inplace=True, ) + cur = model.Relu(cur, cur) # conv 3x3 -> BN -> ReLU cur = model.ConvAffine( cur, - prefix + '_branch2b', + f'{prefix}_branch2b', dim_inner, dim_inner, kernel=3, @@ -252,8 +253,9 @@ def bottleneck_transformation( pad=1 * dilation, dilation=dilation, group=group, - inplace=True + inplace=True, ) + cur = model.Relu(cur, cur) # conv 1x1 -> BN (no ReLU) @@ -261,12 +263,13 @@ def bottleneck_transformation( # gradient computation for graphs like this cur = model.ConvAffine( cur, - prefix + '_branch2c', + f'{prefix}_branch2c', dim_inner, dim_out, kernel=1, stride=1, pad=0, - inplace=False + inplace=False, ) + return cur diff --git a/lib/modeling/detector.py b/lib/modeling/detector.py index 35c28097d..beef6ceea 100644 --- a/lib/modeling/detector.py +++ b/lib/modeling/detector.py @@ -69,13 +69,12 @@ def TrainableParams(self, gpu_id=-1): GPU id. """ return [ - p for p in self.params - if ( - p in self.param_to_grad and # p has a gradient - p not in self.do_not_update_params and # not on the blacklist - (gpu_id == -1 or # filter for gpu assignment, if gpu_id set - str(p).find('gpu_{}'.format(gpu_id)) == 0) - )] + p + for p in self.params + if p in self.param_to_grad + and p not in self.do_not_update_params + and (gpu_id == -1 or str(p).find(f'gpu_{gpu_id}') == 0) + ] def AffineChannel(self, blob_in, blob_out, dim, inplace=False): """Affine transformation to replace BN in networks where BN cannot be @@ -87,17 +86,23 @@ def AffineChannel(self, blob_in, blob_out, dim, inplace=False): param_prefix = blob_out scale = self.create_param( - param_name=param_prefix + '_s', - initializer=initializers.Initializer("ConstantFill", value=1.), + param_name=f'{param_prefix}_s', + initializer=initializers.Initializer("ConstantFill", value=1.0), tags=ParameterTags.WEIGHT, - shape=[dim, ], + shape=[ + dim, + ], ) + bias = self.create_param( - param_name=param_prefix + '_b', - initializer=initializers.Initializer("ConstantFill", value=0.), + param_name=f'{param_prefix}_b', + initializer=initializers.Initializer("ConstantFill", value=0.0), tags=ParameterTags.BIAS, - shape=[dim, ], + shape=[ + dim, + ], ) + if inplace: return self.net.AffineChannel([blob_in, scale, bias], blob_in) else: @@ -200,10 +205,8 @@ def CollectAndDistributeFpnRpnProposals(self): k_min = cfg.FPN.RPN_MIN_LEVEL # Prepare input blobs - rois_names = ['rpn_rois_fpn' + str(l) for l in range(k_min, k_max + 1)] - score_names = [ - 'rpn_roi_probs_fpn' + str(l) for l in range(k_min, k_max + 1) - ] + rois_names = [f'rpn_rois_fpn{str(l)}' for l in range(k_min, k_max + 1)] + score_names = [f'rpn_roi_probs_fpn{str(l)}' for l in range(k_min, k_max + 1)] blobs_in = rois_names + score_names if self.train: blobs_in += ['roidb', 'im_info'] @@ -218,12 +221,10 @@ def CollectAndDistributeFpnRpnProposals(self): ) blobs_out = [core.ScopedBlobReference(b) for b in blobs_out] - outputs = self.net.Python( + return self.net.Python( CollectAndDistributeFpnRpnProposalsOp(self.train).forward )(blobs_in, blobs_out, name=name) - return outputs - def DropoutIfTraining(self, blob_in, dropout_rate): """Add dropout to blob_in if the model is in training mode and dropout_rate is > 0.""" @@ -251,8 +252,7 @@ def RoIFeatureTransform( - Use of FPN or not - Specifics of the transform method """ - assert method in {'RoIPoolF', 'RoIAlign'}, \ - 'Unknown pooling method: {}'.format(method) + assert method in {'RoIPoolF', 'RoIAlign'}, f'Unknown pooling method: {method}' has_argmax = (method == 'RoIPoolF') if isinstance(blobs_in, list): # FPN case: add RoIFeatureTransform to each FPN level @@ -263,10 +263,10 @@ def RoIFeatureTransform( for lvl in range(k_min, k_max + 1): bl_in = blobs_in[k_max - lvl] # blobs_in is in reversed order sc = spatial_scale[k_max - lvl] # in reversed order - bl_rois = blob_rois + '_fpn' + str(lvl) - bl_out = blob_out + '_fpn' + str(lvl) + bl_rois = f'{blob_rois}_fpn{str(lvl)}' + bl_out = f'{blob_out}_fpn{str(lvl)}' bl_out_list.append(bl_out) - bl_argmax = ['_argmax_' + bl_out] if has_argmax else [] + bl_argmax = [f'_argmax_{bl_out}'] if has_argmax else [] self.net.__getattr__(method)( [bl_in, bl_rois], [bl_out] + bl_argmax, pooled_w=resolution, @@ -277,27 +277,29 @@ def RoIFeatureTransform( # The pooled features from all levels are concatenated along the # batch dimension into a single 4D tensor. xform_shuffled, _ = self.net.Concat( - bl_out_list, [blob_out + '_shuffled', '_concat_' + blob_out], - axis=0 + bl_out_list, + [f'{blob_out}_shuffled', f'_concat_{blob_out}'], + axis=0, ) + # Unshuffle to match rois from dataloader - restore_bl = blob_rois + '_idx_restore_int32' - xform_out = self.net.BatchPermutation( + restore_bl = f'{blob_rois}_idx_restore_int32' + return self.net.BatchPermutation( [xform_shuffled, restore_bl], blob_out ) + else: # Single feature level - bl_argmax = ['_argmax_' + blob_out] if has_argmax else [] + bl_argmax = [f'_argmax_{blob_out}'] if has_argmax else [] # sampling_ratio is ignored for RoIPoolF - xform_out = self.net.__getattr__(method)( - [blobs_in, blob_rois], [blob_out] + bl_argmax, + return self.net.__getattr__(method)( + [blobs_in, blob_rois], + [blob_out] + bl_argmax, pooled_w=resolution, pooled_h=resolution, spatial_scale=spatial_scale, - sampling_ratio=sampling_ratio + sampling_ratio=sampling_ratio, ) - # Only return the first blob (the transformed features) - return xform_out def ConvShared( self, @@ -312,9 +314,7 @@ def ConvShared( ): """Add conv op that shares weights and/or biases with another conv op. """ - use_bias = ( - False if ('no_bias' in kwargs and kwargs['no_bias']) else True - ) + use_bias = 'no_bias' not in kwargs or not kwargs['no_bias'] if self.use_cudnn: kwargs['engine'] = 'CUDNN' @@ -322,11 +322,7 @@ def ConvShared( if self.ws_nbytes_limit: kwargs['ws_nbytes_limit'] = self.ws_nbytes_limit - if use_bias: - blobs_in = [blob_in, weight, bias] - else: - blobs_in = [blob_in, weight] - + blobs_in = [blob_in, weight, bias] if use_bias else [blob_in, weight] if 'no_bias' in kwargs: del kwargs['no_bias'] @@ -349,10 +345,7 @@ def BilinearInterpolation( def upsample_filt(size): factor = (size + 1) // 2 - if size % 2 == 1: - center = factor - 1 - else: - center = factor - 0.5 + center = factor - 1 if size % 2 == 1 else factor - 0.5 og = np.ogrid[:size, :size] return ((1 - abs(og[0] - center) / factor) * (1 - abs(og[1] - center) / factor)) @@ -405,10 +398,9 @@ def ConvAffine( # args in the same order of Conv() bias_init=bias_init, no_bias=1 ) - blob_out = self.AffineChannel( + return self.AffineChannel( conv_blob, prefix + suffix, dim=dim_out, inplace=inplace ) - return blob_out def DisableCudnn(self): self.prev_use_cudnn = self.use_cudnn @@ -442,8 +434,7 @@ def _SetNewLr(self, cur_lr, new_lr): """ for i in range(cfg.NUM_GPUS): with c2_utils.CudaScope(i): - workspace.FeedBlob( - 'gpu_{}/lr'.format(i), np.array([new_lr], dtype=np.float32)) + workspace.FeedBlob(f'gpu_{i}/lr', np.array([new_lr], dtype=np.float32)) ratio = _get_lr_change_ratio(cur_lr, new_lr) if cfg.SOLVER.SCALE_MOMENTUM and cur_lr > 1e-7 and \ ratio > cfg.SOLVER.SCALE_MOMENTUM_THRESHOLD: @@ -467,8 +458,12 @@ def _CorrectMomentum(self, correction): with c2_utils.CudaScope(i): for param in self.TrainableParams(gpu_id=i): op = core.CreateOperator( - 'Scale', [param + '_momentum'], [param + '_momentum'], - scale=correction) + 'Scale', + [f'{param}_momentum'], + [f'{param}_momentum'], + scale=correction, + ) + workspace.RunOperatorOnce(op) def GetLossScale(self): @@ -493,7 +488,6 @@ def AddMetrics(self, metrics): def _get_lr_change_ratio(cur_lr, new_lr): eps = 1e-10 - ratio = np.max( + return np.max( (new_lr / np.max((cur_lr, eps)), cur_lr / np.max((new_lr, eps))) ) - return ratio diff --git a/lib/modeling/generate_anchors.py b/lib/modeling/generate_anchors.py index f6b7a1ab6..226895737 100644 --- a/lib/modeling/generate_anchors.py +++ b/lib/modeling/generate_anchors.py @@ -92,15 +92,14 @@ def _mkanchors(ws, hs, x_ctr, y_ctr): """ ws = ws[:, np.newaxis] hs = hs[:, np.newaxis] - anchors = np.hstack( + return np.hstack( ( x_ctr - 0.5 * (ws - 1), y_ctr - 0.5 * (hs - 1), x_ctr + 0.5 * (ws - 1), - y_ctr + 0.5 * (hs - 1) + y_ctr + 0.5 * (hs - 1), ) ) - return anchors def _ratio_enum(anchor, ratios): @@ -110,8 +109,7 @@ def _ratio_enum(anchor, ratios): size_ratios = size / ratios ws = np.round(np.sqrt(size_ratios)) hs = np.round(ws * ratios) - anchors = _mkanchors(ws, hs, x_ctr, y_ctr) - return anchors + return _mkanchors(ws, hs, x_ctr, y_ctr) def _scale_enum(anchor, scales): @@ -119,5 +117,4 @@ def _scale_enum(anchor, scales): w, h, x_ctr, y_ctr = _whctrs(anchor) ws = w * scales hs = h * scales - anchors = _mkanchors(ws, hs, x_ctr, y_ctr) - return anchors + return _mkanchors(ws, hs, x_ctr, y_ctr) diff --git a/lib/modeling/keypoint_rcnn_heads.py b/lib/modeling/keypoint_rcnn_heads.py index 36d891aa9..2635681ed 100644 --- a/lib/modeling/keypoint_rcnn_heads.py +++ b/lib/modeling/keypoint_rcnn_heads.py @@ -65,11 +65,7 @@ def add_keypoint_outputs(model, blob_in, dim): model.Relu('kps_deconv', 'kps_deconv') dim = cfg.KRCNN.DECONV_DIM - if upsample_heatmap: - blob_name = 'kps_score_lowres' - else: - blob_name = 'kps_score' - + blob_name = 'kps_score_lowres' if upsample_heatmap else 'kps_score' if cfg.KRCNN.USE_DECONV_OUTPUT: # Use ConvTranspose to predict heatmaps; results in 2x upsampling blob_out = model.ConvTranspose( @@ -202,15 +198,16 @@ def add_roi_pose_head_v1convX(model, blob_in, dim_in, spatial_scale): for i in range(cfg.KRCNN.NUM_STACKED_CONVS): current = model.Conv( current, - 'conv_fcn' + str(i + 1), + f'conv_fcn{str(i + 1)}', dim_in, hidden_dim, kernel_size, stride=1, pad=pad_size, weight_init=(cfg.KRCNN.CONV_INIT, {'std': 0.01}), - bias_init=('ConstantFill', {'value': 0.}) + bias_init=('ConstantFill', {'value': 0.0}), ) + current = model.Relu(current, current) dim_in = hidden_dim diff --git a/lib/modeling/mask_rcnn_heads.py b/lib/modeling/mask_rcnn_heads.py index 78fd5b789..5af654329 100644 --- a/lib/modeling/mask_rcnn_heads.py +++ b/lib/modeling/mask_rcnn_heads.py @@ -141,15 +141,16 @@ def mask_rcnn_fcn_head_v1upXconvs( for i in range(num_convs): current = model.Conv( current, - '_[mask]_fcn' + str(i + 1), + f'_[mask]_fcn{str(i + 1)}', dim_in, dim_inner, kernel=3, pad=1 * dilation, stride=1, weight_init=(cfg.MRCNN.CONV_INIT, {'std': 0.001}), - bias_init=('ConstantFill', {'value': 0.}) + bias_init=('ConstantFill', {'value': 0.0}), ) + current = model.Relu(current, current) dim_in = dim_inner diff --git a/lib/modeling/model_builder.py b/lib/modeling/model_builder.py index f2ed18d26..e0399afd7 100644 --- a/lib/modeling/model_builder.py +++ b/lib/modeling/model_builder.py @@ -126,10 +126,7 @@ def get_func(func_name): return None new_func_name = modeling.name_compat.get_new_name(func_name) if new_func_name != func_name: - logger.warn( - 'Remapping old function name: {} -> {}'. - format(func_name, new_func_name) - ) + logger.warn(f'Remapping old function name: {func_name} -> {new_func_name}') func_name = new_func_name try: parts = func_name.split('.') @@ -141,7 +138,7 @@ def get_func(func_name): module = importlib.import_module(module_name) return getattr(module, parts[-1]) except Exception: - logger.error('Failed to find function: {}'.format(func_name)) + logger.error(f'Failed to find function: {func_name}') raise @@ -210,14 +207,13 @@ def _single_gpu_build_func(model): spatial_scale_conv ) - if model.train: - loss_gradients = {} - for lg in head_loss_gradients.values(): - if lg is not None: - loss_gradients.update(lg) - return loss_gradients - else: + if not model.train: return None + loss_gradients = {} + for lg in head_loss_gradients.values(): + if lg is not None: + loss_gradients |= lg + return loss_gradients optim.build_data_parallel_model(model, _single_gpu_build_func) return model @@ -247,11 +243,7 @@ def _add_fast_rcnn_head( model, blob_in, dim_in, spatial_scale_in ) fast_rcnn_heads.add_fast_rcnn_outputs(model, blob_frcn, dim_frcn) - if model.train: - loss_gradients = fast_rcnn_heads.add_fast_rcnn_losses(model) - else: - loss_gradients = None - return loss_gradients + return fast_rcnn_heads.add_fast_rcnn_losses(model) if model.train else None def _add_roi_mask_head( @@ -269,19 +261,17 @@ def _add_roi_mask_head( model, blob_mask_head, dim_mask_head ) - if not model.train: # == inference - # Inference uses a cascade of box predictions, then mask predictions. - # This requires separate nets for box and mask prediction. - # So we extract the mask prediction net, store it as its own network, - # then restore model.net to be the bbox-only network - model.mask_net, blob_mask = c2_utils.SuffixNet( - 'mask_net', model.net, len(bbox_net.op), blob_mask - ) - model.net._net = bbox_net - loss_gradients = None - else: - loss_gradients = mask_rcnn_heads.add_mask_rcnn_losses(model, blob_mask) - return loss_gradients + if model.train: + return mask_rcnn_heads.add_mask_rcnn_losses(model, blob_mask) + # Inference uses a cascade of box predictions, then mask predictions. + # This requires separate nets for box and mask prediction. + # So we extract the mask prediction net, store it as its own network, + # then restore model.net to be the bbox-only network + model.mask_net, blob_mask = c2_utils.SuffixNet( + 'mask_net', model.net, len(bbox_net.op), blob_mask + ) + model.net._net = bbox_net + return None def _add_roi_keypoint_head( @@ -299,19 +289,17 @@ def _add_roi_keypoint_head( model, blob_keypoint_head, dim_keypoint_head ) - if not model.train: # == inference - # Inference uses a cascade of box predictions, then keypoint predictions - # This requires separate nets for box and keypoint prediction. - # So we extract the keypoint prediction net, store it as its own - # network, then restore model.net to be the bbox-only network - model.keypoint_net, keypoint_blob_out = c2_utils.SuffixNet( - 'keypoint_net', model.net, len(bbox_net.op), blob_keypoint - ) - model.net._net = bbox_net - loss_gradients = None - else: - loss_gradients = keypoint_rcnn_heads.add_keypoint_losses(model) - return loss_gradients + if model.train: + return keypoint_rcnn_heads.add_keypoint_losses(model) + # Inference uses a cascade of box predictions, then keypoint predictions + # This requires separate nets for box and keypoint prediction. + # So we extract the keypoint prediction net, store it as its own + # network, then restore model.net to be the bbox-only network + model.keypoint_net, keypoint_blob_out = c2_utils.SuffixNet( + 'keypoint_net', model.net, len(bbox_net.op), blob_keypoint + ) + model.net._net = bbox_net + return None def build_generic_rfcn_model(model, add_conv_body_func, dim_reduce=None): diff --git a/lib/modeling/optimizer.py b/lib/modeling/optimizer.py index 18f7f5118..73743058e 100644 --- a/lib/modeling/optimizer.py +++ b/lib/modeling/optimizer.py @@ -57,7 +57,7 @@ def _build_forward_graph(model, single_gpu_build_func): # Build the model on each GPU with correct name and device scoping for gpu_id in range(cfg.NUM_GPUS): with c2_utils.NamedCudaScope(gpu_id): - all_loss_gradients.update(single_gpu_build_func(model)) + all_loss_gradients |= single_gpu_build_func(model) return all_loss_gradients @@ -73,11 +73,9 @@ def _add_allreduce_graph(model): with c2_utils.CudaScope(0): # Iterate over distinct parameter blobs for i in range(params_per_gpu): - # Gradients from all GPUs for this parameter blob - gradients = [ + if gradients := [ model.param_to_grad[p] for p in all_params[i::params_per_gpu] - ] - if len(gradients) > 0: + ]: if cfg.USE_NCCL: model.net.NCCLAllreduce(gradients, gradients) else: @@ -100,12 +98,13 @@ def _add_parameter_update_ops(model, gpu_id): ) for param in model.TrainableParams(gpu_id=gpu_id): - logger.info('param ' + str(param) + ' will be updated') + logger.info(f'param {str(param)} will be updated') param_grad = model.param_to_grad[param] # Initialize momentum vector param_momentum = model.param_init_net.ConstantFill( - [param], param + '_momentum', value=0.0 + [param], f'{param}_momentum', value=0.0 ) + if param in model.biases: # Special treatment for biases (mainly to match historical impl. # details): diff --git a/lib/modeling/retinanet_heads.py b/lib/modeling/retinanet_heads.py index c718be6c0..093f21a4b 100644 --- a/lib/modeling/retinanet_heads.py +++ b/lib/modeling/retinanet_heads.py @@ -35,29 +35,22 @@ def get_retinanet_bias_init(model): """ prior_prob = cfg.RETINANET.PRIOR_PROB scales_per_octave = cfg.RETINANET.SCALES_PER_OCTAVE - aspect_ratios = len(cfg.RETINANET.ASPECT_RATIOS) - if cfg.RETINANET.SOFTMAX: - # Multiclass softmax case - bias = np.zeros((model.num_classes, 1), dtype=np.float32) - bias[0] = np.log( - (model.num_classes - 1) * (1 - prior_prob) / (prior_prob) - ) - bias = np.vstack( - [bias for _ in range(scales_per_octave * aspect_ratios)] - ) - bias_init = ( - 'GivenTensorFill', { - 'values': bias.astype(dtype=np.float32) - } - ) - else: + if not cfg.RETINANET.SOFTMAX: # Per-class sigmoid (binary classification) case - bias_init = ( - 'ConstantFill', { - 'value': -np.log((1 - prior_prob) / prior_prob) - } - ) - return bias_init + return 'ConstantFill', { + 'value': -np.log((1 - prior_prob) / prior_prob) + } + + # Multiclass softmax case + bias = np.zeros((model.num_classes, 1), dtype=np.float32) + bias[0] = np.log( + (model.num_classes - 1) * (1 - prior_prob) / (prior_prob) + ) + aspect_ratios = len(cfg.RETINANET.ASPECT_RATIOS) + bias = np.vstack( + [bias for _ in range(scales_per_octave * aspect_ratios)] + ) + return 'GivenTensorFill', {'values': bias.astype(dtype=np.float32)} def add_fpn_retinanet_outputs(model, blobs_in, dim_in, spatial_scales): @@ -91,76 +84,73 @@ def add_fpn_retinanet_outputs(model, blobs_in, dim_in, spatial_scales): bl_in = blobs_in[k_max - lvl] # blobs_in is in reversed order # classification tower stack convolution starts for nconv in range(cfg.RETINANET.NUM_CONVS): - suffix = 'n{}_fpn{}'.format(nconv, lvl) + suffix = f'n{nconv}_fpn{lvl}' dim_in, dim_out = dim_in, dim_in if lvl == k_min: bl_out = model.Conv( bl_in, - 'retnet_cls_conv_' + suffix, + f'retnet_cls_conv_{suffix}', dim_in, dim_out, 3, stride=1, pad=1, - weight_init=('GaussianFill', { - 'std': 0.01 - }), - bias_init=('ConstantFill', { - 'value': 0. - }) + weight_init=('GaussianFill', {'std': 0.01}), + bias_init=('ConstantFill', {'value': 0.0}), ) + else: bl_out = model.ConvShared( bl_in, - 'retnet_cls_conv_' + suffix, + f'retnet_cls_conv_{suffix}', dim_in, dim_out, 3, stride=1, pad=1, - weight='retnet_cls_conv_n{}_fpn{}_w'.format(nconv, k_min), - bias='retnet_cls_conv_n{}_fpn{}_b'.format(nconv, k_min) + weight=f'retnet_cls_conv_n{nconv}_fpn{k_min}_w', + bias=f'retnet_cls_conv_n{nconv}_fpn{k_min}_b', ) + bl_in = model.Relu(bl_out, bl_out) bl_feat = bl_in # cls tower stack convolution ends. Add the logits layer now if lvl == k_min: retnet_cls_pred = model.Conv( bl_feat, - 'retnet_cls_pred_fpn{}'.format(lvl), + f'retnet_cls_pred_fpn{lvl}', dim_in, cls_pred_dim * A, 3, pad=1, stride=1, - weight_init=('GaussianFill', { - 'std': 0.01 - }), - bias_init=bias_init + weight_init=('GaussianFill', {'std': 0.01}), + bias_init=bias_init, ) + else: retnet_cls_pred = model.ConvShared( bl_feat, - 'retnet_cls_pred_fpn{}'.format(lvl), + f'retnet_cls_pred_fpn{lvl}', dim_in, cls_pred_dim * A, 3, pad=1, stride=1, - weight='retnet_cls_pred_fpn{}_w'.format(k_min), - bias='retnet_cls_pred_fpn{}_b'.format(k_min) + weight=f'retnet_cls_pred_fpn{k_min}_w', + bias=f'retnet_cls_pred_fpn{k_min}_b', ) + if not model.train: if cfg.RETINANET.SOFTMAX: model.net.GroupSpatialSoftmax( retnet_cls_pred, - 'retnet_cls_prob_fpn{}'.format(lvl), - num_classes=cls_pred_dim + f'retnet_cls_prob_fpn{lvl}', + num_classes=cls_pred_dim, ) + else: - model.net.Sigmoid( - retnet_cls_pred, 'retnet_cls_prob_fpn{}'.format(lvl) - ) + model.net.Sigmoid(retnet_cls_pred, f'retnet_cls_prob_fpn{lvl}') if cfg.RETINANET.SHARE_CLS_BBOX_TOWER: bbox_feat_list.append(bl_feat) @@ -172,28 +162,25 @@ def add_fpn_retinanet_outputs(model, blobs_in, dim_in, spatial_scales): for lvl in range(k_min, k_max + 1): bl_in = blobs_in[k_max - lvl] # blobs_in is in reversed order for nconv in range(cfg.RETINANET.NUM_CONVS): - suffix = 'n{}_fpn{}'.format(nconv, lvl) + suffix = f'n{nconv}_fpn{lvl}' dim_in, dim_out = dim_in, dim_in if lvl == k_min: bl_out = model.Conv( bl_in, - 'retnet_bbox_conv_' + suffix, + f'retnet_bbox_conv_{suffix}', dim_in, dim_out, 3, stride=1, pad=1, - weight_init=('GaussianFill', { - 'std': 0.01 - }), - bias_init=('ConstantFill', { - 'value': 0. - }) + weight_init=('GaussianFill', {'std': 0.01}), + bias_init=('ConstantFill', {'value': 0.0}), ) + else: bl_out = model.ConvShared( bl_in, - 'retnet_bbox_conv_' + suffix, + f'retnet_bbox_conv_{suffix}', dim_in, dim_out, 3, @@ -204,8 +191,9 @@ def add_fpn_retinanet_outputs(model, blobs_in, dim_in, spatial_scales): ), bias='retnet_bbox_conv_n{}_fpn{}_b'.format( nconv, k_min - ) + ), ) + bl_in = model.Relu(bl_out, bl_out) # Add octave scales and aspect ratio # At least 1 convolution for dealing different aspect ratios @@ -257,53 +245,56 @@ def add_fpn_retinanet_losses(model): # bbox regression loss - SelectSmoothL1Loss for multiple anchors at a location # ========================================================================== for lvl in range(k_min, k_max + 1): - suffix = 'fpn{}'.format(lvl) + suffix = f'fpn{lvl}' bbox_loss = model.net.SelectSmoothL1Loss( [ - 'retnet_bbox_pred_' + suffix, - 'retnet_roi_bbox_targets_' + suffix, - 'retnet_roi_fg_bbox_locs_' + suffix, 'retnet_fg_num' + f'retnet_bbox_pred_{suffix}', + f'retnet_roi_bbox_targets_{suffix}', + f'retnet_roi_fg_bbox_locs_{suffix}', + 'retnet_fg_num', ], - 'retnet_loss_bbox_' + suffix, + f'retnet_loss_bbox_{suffix}', beta=cfg.RETINANET.BBOX_REG_BETA, - scale=model.GetLossScale() * cfg.RETINANET.BBOX_REG_WEIGHT + scale=model.GetLossScale() * cfg.RETINANET.BBOX_REG_WEIGHT, ) + gradients.append(bbox_loss) - losses.append('retnet_loss_bbox_' + suffix) + losses.append(f'retnet_loss_bbox_{suffix}') # ========================================================================== # cls loss - depends on softmax/sigmoid outputs # ========================================================================== for lvl in range(k_min, k_max + 1): - suffix = 'fpn{}'.format(lvl) - cls_lvl_logits = 'retnet_cls_pred_' + suffix + suffix = f'fpn{lvl}' + cls_lvl_logits = f'retnet_cls_pred_{suffix}' if not cfg.RETINANET.SOFTMAX: cls_focal_loss = model.net.SigmoidFocalLoss( [ - cls_lvl_logits, 'retnet_cls_labels_' + suffix, - 'retnet_fg_num' + cls_lvl_logits, + f'retnet_cls_labels_{suffix}', + 'retnet_fg_num', ], - ['fl_{}'.format(suffix)], + [f'fl_{suffix}'], gamma=cfg.RETINANET.LOSS_GAMMA, alpha=cfg.RETINANET.LOSS_ALPHA, - scale=model.GetLossScale() + scale=model.GetLossScale(), ) - gradients.append(cls_focal_loss) - losses.append('fl_{}'.format(suffix)) + else: cls_focal_loss, gated_prob = model.net.SoftmaxFocalLoss( [ - cls_lvl_logits, 'retnet_cls_labels_' + suffix, - 'retnet_fg_num' + cls_lvl_logits, + f'retnet_cls_labels_{suffix}', + 'retnet_fg_num', ], - ['fl_{}'.format(suffix), 'retnet_prob_{}'.format(suffix)], + [f'fl_{suffix}', f'retnet_prob_{suffix}'], gamma=cfg.RETINANET.LOSS_GAMMA, alpha=cfg.RETINANET.LOSS_ALPHA, scale=model.GetLossScale(), ) - gradients.append(cls_focal_loss) - losses.append('fl_{}'.format(suffix)) - loss_gradients.update(blob_utils.get_loss_gradients(model, gradients)) + gradients.append(cls_focal_loss) + losses.append(f'fl_{suffix}') + loss_gradients |= blob_utils.get_loss_gradients(model, gradients) model.AddLosses(losses) return loss_gradients diff --git a/lib/modeling/rpn_heads.py b/lib/modeling/rpn_heads.py index 8b3c4b50f..41fd1b6a6 100644 --- a/lib/modeling/rpn_heads.py +++ b/lib/modeling/rpn_heads.py @@ -131,8 +131,9 @@ def add_single_scale_rpn_losses(model): ) for key in ('targets', 'inside_weights', 'outside_weights'): model.net.SpatialNarrowAs( - ['rpn_bbox_' + key + '_wide', 'rpn_bbox_pred'], 'rpn_bbox_' + key + [f'rpn_bbox_{key}_wide', 'rpn_bbox_pred'], f'rpn_bbox_{key}' ) + loss_rpn_cls = model.net.SigmoidCrossEntropyLoss( ['rpn_cls_logits', 'rpn_labels_int32'], 'loss_rpn_cls', diff --git a/lib/ops/generate_proposals.py b/lib/ops/generate_proposals.py index 350225c42..02b0241bd 100644 --- a/lib/ops/generate_proposals.py +++ b/lib/ops/generate_proposals.py @@ -177,7 +177,9 @@ def _filter_boxes(boxes, min_size, im_info): hs = boxes[:, 3] - boxes[:, 1] + 1 x_ctr = boxes[:, 0] + ws / 2. y_ctr = boxes[:, 1] + hs / 2. - keep = np.where( - (ws >= min_size) & (hs >= min_size) & - (x_ctr < im_info[1]) & (y_ctr < im_info[0]))[0] - return keep + return np.where( + (ws >= min_size) + & (hs >= min_size) + & (x_ctr < im_info[1]) + & (y_ctr < im_info[0]) + )[0] diff --git a/lib/roi_data/fast_rcnn.py b/lib/roi_data/fast_rcnn.py index d76566a33..98e48fa6f 100644 --- a/lib/roi_data/fast_rcnn.py +++ b/lib/roi_data/fast_rcnn.py @@ -47,7 +47,6 @@ def get_fast_rcnn_blob_names(is_training=True): # labels_int32 blob: R categorical labels in [0, ..., K] for K # foreground classes plus background blob_names += ['labels_int32'] - if is_training: # bbox_targets blob: R bounding-box regression targets with 4 # targets per class blob_names += ['bbox_targets'] @@ -55,35 +54,35 @@ def get_fast_rcnn_blob_names(is_training=True): # this binary vector sepcifies the subset of active targets blob_names += ['bbox_inside_weights'] blob_names += ['bbox_outside_weights'] - if is_training and cfg.MODEL.MASK_ON: - # 'mask_rois': RoIs sampled for training the mask prediction branch. - # Shape is (#masks, 5) in format (batch_idx, x1, y1, x2, y2). - blob_names += ['mask_rois'] - # 'roi_has_mask': binary labels for the RoIs specified in 'rois' - # indicating if each RoI has a mask or not. Note that in some cases - # a *bg* RoI will have an all -1 (ignore) mask associated with it in - # the case that no fg RoIs can be sampled. Shape is (batchsize). - blob_names += ['roi_has_mask_int32'] - # 'masks_int32' holds binary masks for the RoIs specified in - # 'mask_rois'. Shape is (#fg, M * M) where M is the ground truth - # mask size. - blob_names += ['masks_int32'] - if is_training and cfg.MODEL.KEYPOINTS_ON: - # 'keypoint_rois': RoIs sampled for training the keypoint prediction - # branch. Shape is (#instances, 5) in format (batch_idx, x1, y1, x2, - # y2). - blob_names += ['keypoint_rois'] - # 'keypoint_locations_int32': index of keypoint in - # KRCNN.HEATMAP_SIZE**2 sized array. Shape is (#instances). Used in - # SoftmaxWithLoss. - blob_names += ['keypoint_locations_int32'] - # 'keypoint_weights': weight assigned to each target in - # 'keypoint_locations_int32'. Shape is (#instances). Used in - # SoftmaxWithLoss. - blob_names += ['keypoint_weights'] - # 'keypoint_loss_normalizer': optional normalization factor to use if - # cfg.KRCNN.NORMALIZE_BY_VISIBLE_KEYPOINTS is False. - blob_names += ['keypoint_loss_normalizer'] + if cfg.MODEL.MASK_ON: + # 'mask_rois': RoIs sampled for training the mask prediction branch. + # Shape is (#masks, 5) in format (batch_idx, x1, y1, x2, y2). + blob_names += ['mask_rois'] + # 'roi_has_mask': binary labels for the RoIs specified in 'rois' + # indicating if each RoI has a mask or not. Note that in some cases + # a *bg* RoI will have an all -1 (ignore) mask associated with it in + # the case that no fg RoIs can be sampled. Shape is (batchsize). + blob_names += ['roi_has_mask_int32'] + # 'masks_int32' holds binary masks for the RoIs specified in + # 'mask_rois'. Shape is (#fg, M * M) where M is the ground truth + # mask size. + blob_names += ['masks_int32'] + if cfg.MODEL.KEYPOINTS_ON: + # 'keypoint_rois': RoIs sampled for training the keypoint prediction + # branch. Shape is (#instances, 5) in format (batch_idx, x1, y1, x2, + # y2). + blob_names += ['keypoint_rois'] + # 'keypoint_locations_int32': index of keypoint in + # KRCNN.HEATMAP_SIZE**2 sized array. Shape is (#instances). Used in + # SoftmaxWithLoss. + blob_names += ['keypoint_locations_int32'] + # 'keypoint_weights': weight assigned to each target in + # 'keypoint_locations_int32'. Shape is (#instances). Used in + # SoftmaxWithLoss. + blob_names += ['keypoint_weights'] + # 'keypoint_loss_normalizer': optional normalization factor to use if + # cfg.KRCNN.NORMALIZE_BY_VISIBLE_KEYPOINTS is False. + blob_names += ['keypoint_loss_normalizer'] if cfg.FPN.FPN_ON and cfg.FPN.MULTILEVEL_ROIS: # Support for FPN multi-level rois without bbox reg isn't # implemented (... and may never be implemented) @@ -91,16 +90,16 @@ def get_fast_rcnn_blob_names(is_training=True): k_min = cfg.FPN.ROI_MIN_LEVEL # Same format as rois blob, but one per FPN level for lvl in range(k_min, k_max + 1): - blob_names += ['rois_fpn' + str(lvl)] + blob_names += [f'rois_fpn{str(lvl)}'] blob_names += ['rois_idx_restore_int32'] if is_training: if cfg.MODEL.MASK_ON: for lvl in range(k_min, k_max + 1): - blob_names += ['mask_rois_fpn' + str(lvl)] + blob_names += [f'mask_rois_fpn{str(lvl)}'] blob_names += ['mask_rois_idx_restore_int32'] if cfg.MODEL.KEYPOINTS_ON: for lvl in range(k_min, k_max + 1): - blob_names += ['keypoint_rois_fpn' + str(lvl)] + blob_names += [f'keypoint_rois_fpn{str(lvl)}'] blob_names += ['keypoint_rois_idx_restore_int32'] return blob_names diff --git a/lib/roi_data/loader.py b/lib/roi_data/loader.py index ec55c475c..e7ecc80b2 100644 --- a/lib/roi_data/loader.py +++ b/lib/roi_data/loader.py @@ -83,7 +83,7 @@ def __init__( self._blobs_queue_capacity = blobs_queue_capacity # Random queue name in case one instantiates multple RoIDataLoaders self._loader_id = uuid.uuid4() - self._blobs_queue_name = 'roi_blobs_queue_{}'.format(self._loader_id) + self._blobs_queue_name = f'roi_blobs_queue_{self._loader_id}' # Loader threads construct (partial) minibatches and put them on the # minibatch queue self._num_loaders = num_loaders @@ -103,9 +103,11 @@ def minibatch_loader_thread(self): # self.get_output_names ordered_blobs = OrderedDict() for key in self.get_output_names(): - assert blobs[key].dtype in (np.int32, np.float32), \ - 'Blob {} of dtype {} must have dtype of ' \ - 'np.int32 or np.float32'.format(key, blobs[key].dtype) + assert blobs[key].dtype in ( + np.int32, + np.float32, + ), f'Blob {key} of dtype {blobs[key].dtype} must have dtype of np.int32 or np.float32' + ordered_blobs[key] = blobs[key] coordinated_put( self.coordinator, self._minibatch_queue, ordered_blobs @@ -120,9 +122,7 @@ def enqueue_blobs_thread(self, gpu_id, blob_names): logger.warning('Mini-batch queue is empty') blobs = coordinated_get(self.coordinator, self._minibatch_queue) self.enqueue_blobs(gpu_id, blob_names, blobs.values()) - logger.debug( - 'batch queue size {}'.format(self._minibatch_queue.qsize()) - ) + logger.debug(f'batch queue size {self._minibatch_queue.qsize()}') logger.info('Stopping enqueue thread') def get_next_minibatch(self): @@ -180,24 +180,22 @@ def enqueue_blobs(self, gpu_id, blob_names, blobs): assert len(blob_names) == len(blobs) t = time.time() dev = c2_utils.CudaDevice(gpu_id) - queue_name = 'gpu_{}/{}'.format(gpu_id, self._blobs_queue_name) - blob_names = ['gpu_{}/{}'.format(gpu_id, b) for b in blob_names] + queue_name = f'gpu_{gpu_id}/{self._blobs_queue_name}' + blob_names = [f'gpu_{gpu_id}/{b}' for b in blob_names] for (blob_name, blob) in zip(blob_names, blobs): workspace.FeedBlob(blob_name, blob, device_option=dev) - logger.debug( - 'enqueue_blobs {}: workspace.FeedBlob: {}'. - format(gpu_id, time.time() - t) - ) + logger.debug(f'enqueue_blobs {gpu_id}: workspace.FeedBlob: {time.time() - t}') t = time.time() op = core.CreateOperator( - 'SafeEnqueueBlobs', [queue_name] + blob_names, - blob_names + [queue_name + '_enqueue_status'], - device_option=dev + 'SafeEnqueueBlobs', + [queue_name] + blob_names, + blob_names + [f'{queue_name}_enqueue_status'], + device_option=dev, ) + workspace.RunOperatorOnce(op) logger.debug( - 'enqueue_blobs {}: workspace.RunOperatorOnce: {}'. - format(gpu_id, time.time() - t) + f'enqueue_blobs {gpu_id}: workspace.RunOperatorOnce: {time.time() - t}' ) def create_threads(self): @@ -261,7 +259,7 @@ def create_blobs_queues(self): def close_blobs_queues(self): """Close a BlobsQueue.""" for gpu_id in range(self._num_gpus): - with core.NameScope('gpu_{}'.format(gpu_id)): + with core.NameScope(f'gpu_{gpu_id}'): workspace.RunOperatorOnce( core.CreateOperator( 'CloseBlobsQueue', [self._blobs_queue_name], [] @@ -270,9 +268,7 @@ def close_blobs_queues(self): def create_enqueue_blobs(self): blob_names = self.get_output_names() - enqueue_blob_names = [ - '{}_enqueue_{}'.format(b, self._loader_id) for b in blob_names - ] + enqueue_blob_names = [f'{b}_enqueue_{self._loader_id}' for b in blob_names] for gpu_id in range(self._num_gpus): with c2_utils.NamedCudaScope(gpu_id): for blob in enqueue_blob_names: diff --git a/lib/roi_data/mask_rcnn.py b/lib/roi_data/mask_rcnn.py index 147063e91..76ce4a013 100644 --- a/lib/roi_data/mask_rcnn.py +++ b/lib/roi_data/mask_rcnn.py @@ -116,11 +116,11 @@ def _expand_to_class_specific_mask_targets(masks, mask_class_labels): for i in range(masks.shape[0]): cls = int(mask_class_labels[i]) - start = M**2 * cls - end = start + M**2 # Ignore background instance # (only happens when there is no fg samples in an image) if cls > 0: + start = M**2 * cls + end = start + M**2 mask_targets[i, start:end] = masks[i, :] return mask_targets diff --git a/lib/roi_data/retinanet.py b/lib/roi_data/retinanet.py index 20d12852d..01ca2fe21 100644 --- a/lib/roi_data/retinanet.py +++ b/lib/roi_data/retinanet.py @@ -66,12 +66,13 @@ def get_retinanet_blob_names(is_training=True): if is_training: blob_names += ['retnet_fg_num', 'retnet_bg_num'] for lvl in range(cfg.FPN.RPN_MIN_LEVEL, cfg.FPN.RPN_MAX_LEVEL + 1): - suffix = 'fpn{}'.format(lvl) + suffix = f'fpn{lvl}' blob_names += [ - 'retnet_cls_labels_' + suffix, - 'retnet_roi_bbox_targets_' + suffix, - 'retnet_roi_fg_bbox_locs_' + suffix, + f'retnet_cls_labels_{suffix}', + f'retnet_roi_bbox_targets_{suffix}', + f'retnet_roi_fg_bbox_locs_{suffix}', ] + return blob_names @@ -121,7 +122,7 @@ def add_retinanet_blobs(blobs, im_scales, roidb, image_width, image_height): # the way it stacks is: # [[anchors for image1] + [anchors for images 2]] level = int(np.log2(foa.stride)) - key = '{}_fpn{}'.format(k, level) + key = f'{k}_fpn{level}' if k == 'retnet_roi_fg_bbox_locs': v[:, 0] = im_i # loc_stride: 80 * 4 if cls_specific else 4 @@ -147,7 +148,7 @@ def add_retinanet_blobs(blobs, im_scales, roidb, image_width, image_height): for k, v in blobs.items(): if isinstance(v, list) and len(v) > 0: # compute number of anchors - A = int(len(v) / N) + A = len(v) // N # for the cls branch labels [per fpn level], # we have blobs['retnet_cls_labels_fpn{}'] as a list until this step # and length of this list is N x A where @@ -161,10 +162,7 @@ def add_retinanet_blobs(blobs, im_scales, roidb, image_width, image_height): # and then concatenate the two images to get the 2 x 9 x H x W if k.find('retnet_cls_labels') >= 0: - tmp = [] - # concat anchors within an image - for i in range(0, len(v), A): - tmp.append(np.concatenate(v[i: i + A], axis=1)) + tmp = [np.concatenate(v[i: i + A], axis=1) for i in range(0, len(v), A)] # concat images blobs[k] = np.concatenate(tmp, axis=0) else: @@ -182,16 +180,15 @@ def add_retinanet_blobs(blobs, im_scales, roidb, image_width, image_height): def _get_retinanet_blobs( foas, all_anchors, gt_boxes, gt_classes, im_width, im_height): total_anchors = all_anchors.shape[0] - logger.debug('Getting mad blobs: im_height {} im_width: {}'.format( - im_height, im_width)) + logger.debug(f'Getting mad blobs: im_height {im_height} im_width: {im_width}') inds_inside = np.arange(all_anchors.shape[0]) anchors = all_anchors num_inside = len(inds_inside) - logger.debug('total_anchors: {}'.format(total_anchors)) - logger.debug('inds_inside: {}'.format(num_inside)) - logger.debug('anchors.shape: {}'.format(anchors.shape)) + logger.debug(f'total_anchors: {total_anchors}') + logger.debug(f'inds_inside: {num_inside}') + logger.debug(f'anchors.shape: {anchors.shape}') # Compute anchor labels: # label=1 is positive, 0 is negative, -1 is don't care (ignore) diff --git a/lib/roi_data/rpn.py b/lib/roi_data/rpn.py index bb27a2db8..a77c990e3 100644 --- a/lib/roi_data/rpn.py +++ b/lib/roi_data/rpn.py @@ -43,11 +43,12 @@ def get_rpn_blob_names(is_training=True): # Same format as RPN blobs, but one per FPN level for lvl in range(cfg.FPN.RPN_MIN_LEVEL, cfg.FPN.RPN_MAX_LEVEL + 1): blob_names += [ - 'rpn_labels_int32_wide_fpn' + str(lvl), - 'rpn_bbox_targets_wide_fpn' + str(lvl), - 'rpn_bbox_inside_weights_wide_fpn' + str(lvl), - 'rpn_bbox_outside_weights_wide_fpn' + str(lvl) + f'rpn_labels_int32_wide_fpn{str(lvl)}', + f'rpn_bbox_targets_wide_fpn{str(lvl)}', + f'rpn_bbox_inside_weights_wide_fpn{str(lvl)}', + f'rpn_bbox_outside_weights_wide_fpn{str(lvl)}', ] + else: # Single level RPN blobs blob_names += [ @@ -106,7 +107,7 @@ def add_rpn_blobs(blobs, im_scales, roidb): ) for i, lvl in enumerate(range(k_min, k_max + 1)): for k, v in rpn_blobs[i].items(): - blobs[k + '_fpn' + str(lvl)].append(v) + blobs[f'{k}_fpn{str(lvl)}'].append(v) else: # Classical RPN, applied to a single feature level rpn_blobs = _get_rpn_blobs( @@ -155,9 +156,9 @@ def _get_rpn_blobs(im_height, im_width, foas, all_anchors, gt_boxes): anchors = all_anchors num_inside = len(inds_inside) - logger.debug('total_anchors: {}'.format(total_anchors)) - logger.debug('inds_inside: {}'.format(num_inside)) - logger.debug('anchors.shape: {}'.format(anchors.shape)) + logger.debug(f'total_anchors: {total_anchors}') + logger.debug(f'inds_inside: {num_inside}') + logger.debug(f'anchors.shape: {anchors.shape}') # Compute anchor labels: # label=1 is positive, 0 is negative, -1 is don't care (ignore) diff --git a/lib/utils/blob.py b/lib/utils/blob.py index fce46aae4..a2f96f56b 100644 --- a/lib/utils/blob.py +++ b/lib/utils/blob.py @@ -78,8 +78,8 @@ def prep_im_for_blob(im, pixel_means, target_sizes, max_size): im = im.astype(np.float32, copy=False) im -= pixel_means im_shape = im.shape - im_size_min = np.min(im_shape[0:2]) - im_size_max = np.max(im_shape[0:2]) + im_size_min = np.min(im_shape[:2]) + im_size_max = np.max(im_shape[:2]) ims = [] im_scales = [] @@ -132,7 +132,7 @@ def get_loss_gradients(model, loss_blobs): """Generate a gradient of 1 for each loss specified in 'loss_blobs'""" loss_gradients = {} for b in loss_blobs: - loss_grad = model.net.ConstantFill(b, [b + '_grad'], value=1.0) + loss_grad = model.net.ConstantFill(b, [f'{b}_grad'], value=1.0) loss_gradients[str(b)] = str(loss_grad) return loss_gradients diff --git a/lib/utils/boxes.py b/lib/utils/boxes.py index dec87befc..b03d99402 100644 --- a/lib/utils/boxes.py +++ b/lib/utils/boxes.py @@ -109,8 +109,7 @@ def filter_small_boxes(boxes, min_size): """Keep boxes with width and height both greater than min_size.""" w = boxes[:, 2] - boxes[:, 0] + 1 h = boxes[:, 3] - boxes[:, 1] + 1 - keep = np.where((w > min_size) & (h > min_size))[0] - return keep + return np.where((w > min_size) & (h > min_size))[0] def clip_boxes_to_image(boxes, height, width): @@ -219,9 +218,9 @@ def bbox_transform_inv(boxes, gt_boxes, weights=(1.0, 1.0, 1.0, 1.0)): targets_dw = ww * np.log(gt_widths / ex_widths) targets_dh = wh * np.log(gt_heights / ex_heights) - targets = np.vstack((targets_dx, targets_dy, targets_dw, - targets_dh)).transpose() - return targets + return np.vstack( + (targets_dx, targets_dy, targets_dw, targets_dh) + ).transpose() def expand_boxes(boxes, scale): @@ -304,18 +303,14 @@ def box_voting(top_dets, all_dets, thresh, scoring_method='ID', beta=1.0): elif scoring_method == 'QUASI_SUM': top_dets_out[k, 4] = ws.sum() / float(len(ws))**beta else: - raise NotImplementedError( - 'Unknown scoring method {}'.format(scoring_method) - ) + raise NotImplementedError(f'Unknown scoring method {scoring_method}') return top_dets_out def nms(dets, thresh): """Apply classic DPM-style greedy NMS.""" - if dets.shape[0] == 0: - return [] - return cython_nms.nms(dets, thresh) + return [] if dets.shape[0] == 0 else cython_nms.nms(dets, thresh) def soft_nms( @@ -326,7 +321,7 @@ def soft_nms( return dets, [] methods = {'hard': 0, 'linear': 1, 'gaussian': 2} - assert method in methods, 'Unknown soft_nms method: {}'.format(method) + assert method in methods, f'Unknown soft_nms method: {method}' dets, keep = cython_nms.soft_nms( np.ascontiguousarray(dets, dtype=np.float32), diff --git a/lib/utils/env.py b/lib/utils/env.py index c4f9e913f..e145f2c13 100644 --- a/lib/utils/env.py +++ b/lib/utils/env.py @@ -57,10 +57,8 @@ def import_nccl_ops(): def get_caffe2_dir(): """Retrieve Caffe2 dir path.""" _fp, c2_path, _desc = imp.find_module('caffe2') - assert os.path.exists(c2_path), \ - 'Caffe2 not found at \'{}\''.format(c2_path) - c2_dir = os.path.dirname(os.path.abspath(c2_path)) - return c2_dir + assert os.path.exists(c2_path), f"Caffe2 not found at '{c2_path}'" + return os.path.dirname(os.path.abspath(c2_path)) def get_detectron_ops_lib(): @@ -68,9 +66,10 @@ def get_detectron_ops_lib(): c2_dir = get_caffe2_dir() detectron_ops_lib = os.path.join( c2_dir, 'lib/libcaffe2_detectron_ops_gpu.so') - assert os.path.exists(detectron_ops_lib), \ - ('Detectron ops lib not found at \'{}\'; make sure that your Caffe2 ' - 'version includes Detectron module').format(detectron_ops_lib) + assert os.path.exists( + detectron_ops_lib + ), f"Detectron ops lib not found at '{detectron_ops_lib}'; make sure that your Caffe2 version includes Detectron module" + return detectron_ops_lib @@ -79,6 +78,8 @@ def get_custom_ops_lib(): lib_dir, _utils = os.path.split(os.path.dirname(__file__)) custom_ops_lib = os.path.join( lib_dir, 'build/libcaffe2_detectron_custom_ops_gpu.so') - assert os.path.exists(custom_ops_lib), \ - 'Custom ops lib not found at \'{}\''.format(custom_ops_lib) + assert os.path.exists( + custom_ops_lib + ), f"Custom ops lib not found at '{custom_ops_lib}'" + return custom_ops_lib diff --git a/lib/utils/image.py b/lib/utils/image.py index f7a5d3652..4293d3939 100644 --- a/lib/utils/image.py +++ b/lib/utils/image.py @@ -28,8 +28,7 @@ def aspect_ratio_rel(im, aspect_ratio): """Performs width-relative aspect ratio transformation.""" im_h, im_w = im.shape[:2] im_ar_w = int(round(aspect_ratio * im_w)) - im_ar = cv2.resize(im, dsize=(im_ar_w, im_h)) - return im_ar + return cv2.resize(im, dsize=(im_ar_w, im_h)) def aspect_ratio_abs(im, aspect_ratio): @@ -41,5 +40,4 @@ def aspect_ratio_abs(im, aspect_ratio): im_ar_h = np.sqrt(im_area / aspect_ratio) assert np.isclose(im_ar_w / im_ar_h, aspect_ratio) - im_ar = cv2.resize(im, dsize=(int(im_ar_w), int(im_ar_h))) - return im_ar + return cv2.resize(im, dsize=(int(im_ar_w), int(im_ar_h))) diff --git a/lib/utils/io.py b/lib/utils/io.py index cdd51eec9..69b6d56ba 100644 --- a/lib/utils/io.py +++ b/lib/utils/io.py @@ -51,9 +51,10 @@ def cache_url(url_or_file, cache_dir): return url_or_file url = url_or_file - assert url.startswith(_DETECTRON_S3_BASE_URL), \ - ('Detectron only automatically caches URLs in the Detectron S3 ' - 'bucket: {}').format(_DETECTRON_S3_BASE_URL) + assert url.startswith( + _DETECTRON_S3_BASE_URL + ), f'Detectron only automatically caches URLs in the Detectron S3 bucket: {_DETECTRON_S3_BASE_URL}' + cache_file_path = url.replace(_DETECTRON_S3_BASE_URL, cache_dir) if os.path.exists(cache_file_path): @@ -64,7 +65,7 @@ def cache_url(url_or_file, cache_dir): if not os.path.exists(cache_file_dir): os.makedirs(cache_file_dir) - logger.info('Downloading remote file {} to {}'.format(url, cache_file_path)) + logger.info(f'Downloading remote file {url} to {cache_file_path}') download_url(url, cache_file_path) assert_cache_file_is_ok(url, cache_file_path) return cache_file_path @@ -76,12 +77,9 @@ def assert_cache_file_is_ok(url, file_path): # return local path cache_file_md5sum = _get_file_md5sum(file_path) ref_md5sum = _get_reference_md5sum(url) - assert cache_file_md5sum == ref_md5sum, \ - ('Target URL {} appears to be downloaded to the local cache file ' - '{}, but the md5 hash of the local file does not match the ' - 'reference (actual: {} vs. expected: {}). You may wish to delete ' - 'the cached file and try again to trigger automatic ' - 'download.').format(url, file_path, cache_file_md5sum, ref_md5sum) + assert ( + cache_file_md5sum == ref_md5sum + ), f'Target URL {url} appears to be downloaded to the local cache file {file_path}, but the md5 hash of the local file does not match the reference (actual: {cache_file_md5sum} vs. expected: {ref_md5sum}). You may wish to delete the cached file and try again to trigger automatic download.' def _progress_bar(count, total): @@ -139,6 +137,5 @@ def _get_file_md5sum(file_name): def _get_reference_md5sum(url): """By convention the md5 hash for url is stored in url + '.md5sum'.""" - url_md5sum = url + '.md5sum' - md5sum = urllib2.urlopen(url_md5sum).read().strip() - return md5sum + url_md5sum = f'{url}.md5sum' + return urllib2.urlopen(url_md5sum).read().strip() diff --git a/lib/utils/logging.py b/lib/utils/logging.py index 992ea148d..74c899e79 100644 --- a/lib/utils/logging.py +++ b/lib/utils/logging.py @@ -77,5 +77,4 @@ def setup_logging(name): # logging.basicConfig() from blocking our logging setup logging.root.handlers = [] logging.basicConfig(level=logging.INFO, format=FORMAT, stream=sys.stdout) - logger = logging.getLogger(name) - return logger + return logging.getLogger(name) diff --git a/lib/utils/lr_policy.py b/lib/utils/lr_policy.py index b26aaf714..4194c9b97 100644 --- a/lib/utils/lr_policy.py +++ b/lib/utils/lr_policy.py @@ -38,7 +38,7 @@ def get_lr_at_iter(it): alpha = it / cfg.SOLVER.WARM_UP_ITERS warmup_factor = cfg.SOLVER.WARM_UP_FACTOR * (1 - alpha) + alpha else: - raise KeyError('Unknown SOLVER.WARM_UP_METHOD: {}'.format(method)) + raise KeyError(f'Unknown SOLVER.WARM_UP_METHOD: {method}') lr *= warmup_factor return np.float32(lr) @@ -106,9 +106,8 @@ def get_step_index(cur_iter): def get_lr_func(): - policy = 'lr_func_' + cfg.SOLVER.LR_POLICY + policy = f'lr_func_{cfg.SOLVER.LR_POLICY}' if policy not in globals(): - raise NotImplementedError( - 'Unknown LR policy: {}'.format(cfg.SOLVER.LR_POLICY)) + raise NotImplementedError(f'Unknown LR policy: {cfg.SOLVER.LR_POLICY}') else: return globals()[policy] diff --git a/lib/utils/model_convert_utils.py b/lib/utils/model_convert_utils.py index 17752db2b..5d81025d2 100644 --- a/lib/utils/model_convert_utils.py +++ b/lib/utils/model_convert_utils.py @@ -39,7 +39,7 @@ def __init__(self, **kwargs): self.cond = None self.reverse = False - assert all([x in self.__dict__ for x in kwargs]) + assert all(x in self.__dict__ for x in kwargs) self.__dict__.update(kwargs) def check(self, op): @@ -71,9 +71,7 @@ def op_filter(**filter_args): def actual_decorator(f): @wraps(f) def wrapper(op, **params): - if not filter_op(op, **filter_args): - return None - return f(op, **params) + return f(op, **params) if filter_op(op, **filter_args) else None return wrapper return actual_decorator @@ -96,7 +94,7 @@ def convert_op_in_ops(ops_ref, func_or_list): func = func_or_list if isinstance(func_or_list, list): func = op_func_chain(func_or_list) - ops = [op for op in ops_ref] + ops = list(ops_ref) converted_ops = [] for op in ops: new_ops = func(op) @@ -114,10 +112,7 @@ def convert_op_in_proto(proto, func_or_list): def get_op_arg(op, arg_name): - for x in op.arg: - if x.name == arg_name: - return x - return None + return next((x for x in op.arg if x.name == arg_name), None) def get_op_arg_valf(op, arg_name, default_val): @@ -129,7 +124,7 @@ def update_mobile_engines(net): for op in net.op: if op.type == "Conv": op.engine = "NNPACK" - if op.type == "ConvTranspose": + elif op.type == "ConvTranspose": op.engine = "BLOCK" @@ -142,11 +137,11 @@ def pairwise(iterable): def blob_uses(net, blob): - u = [] - for i, op in enumerate(net.op): - if blob in op.input or blob in op.control_input: - u.append(i) - return u + return [ + i + for i, op in enumerate(net.op) + if blob in op.input or blob in op.control_input + ] def fuse_first_affine(net, params, removed_tensors): @@ -308,7 +303,7 @@ def gen_init_net_from_blobs(blobs, blobs_to_use=None, excluded_blobs=None): ''' Generate an initialization net based on a blob dict ''' ret = caffe2_pb2.NetDef() if blobs_to_use is None: - blobs_to_use = {x for x in blobs} + blobs_to_use = set(blobs) else: blobs_to_use = copy.deepcopy(blobs_to_use) if excluded_blobs is not None: @@ -316,8 +311,10 @@ def gen_init_net_from_blobs(blobs, blobs_to_use=None, excluded_blobs=None): for name in blobs_to_use: blob = blobs[name] if isinstance(blob, str): - print('Blob {} with type {} is not supported in generating init net,' - ' skipped.'.format(name, type(blob))) + print( + f'Blob {name} with type {type(blob)} is not supported in generating init net, skipped.' + ) + continue add_tensor(ret, name, blob) @@ -336,8 +333,7 @@ def get_ws_blobs(blob_names=None): def get_device_option_cpu(): - device_option = core.DeviceOption(caffe2_pb2.CPU) - return device_option + return core.DeviceOption(caffe2_pb2.CPU) def get_device_option_cuda(gpu_id=0): @@ -366,22 +362,24 @@ def compare_model(model1_func, model2_func, test_image, check_blobs): print('Running the second model...') res2 = model2_func(test_image, check_blobs) for idx in range(len(cb1)): - print('Checking {} -> {}...'.format(cb1[idx], cb2[idx])) + print(f'Checking {cb1[idx]} -> {cb2[idx]}...') n1, n2 = cb1[idx], cb2[idx] r1 = res1[n1] if n1 in res1 else None r2 = res2[n2] if n2 in res2 else None - assert r1 is not None or r2 is None, \ - "Blob {} in model1 is None".format(n1) - assert r2 is not None or r1 is None, \ - "Blob {} in model2 is None".format(n2) - assert r1.shape == r2.shape, \ - "Blob {} and {} shape mismatched: {} vs {}".format( - n1, n2, r1.shape, r2.shape) + assert r1 is not None or r2 is None, f"Blob {n1} in model1 is None" + assert r2 is not None or r1 is None, f"Blob {n2} in model2 is None" + assert ( + r1.shape == r2.shape + ), f"Blob {n1} and {n2} shape mismatched: {r1.shape} vs {r2.shape}" + np.testing.assert_array_almost_equal( - r1, r2, decimal=3, - err_msg='{} and {} not matched. Max diff: {}'.format( - n1, n2, np.amax(np.absolute(r1 - r2)))) + r1, + r2, + decimal=3, + err_msg=f'{n1} and {n2} not matched. Max diff: {np.amax(np.absolute(r1 - r2))}', + ) + return True @@ -391,16 +389,15 @@ def save_graph(net, file_name, graph_name="net", op_only=True): from caffe2.python import net_drawer graph = None ops = net.op - if not op_only: - graph = net_drawer.GetPydotGraph( - ops, graph_name, - rankdir="TB") - else: - graph = net_drawer.GetPydotGraphMinimal( - ops, graph_name, - rankdir="TB", minimal_dependency=True) + graph = ( + net_drawer.GetPydotGraphMinimal( + ops, graph_name, rankdir="TB", minimal_dependency=True + ) + if op_only + else net_drawer.GetPydotGraph(ops, graph_name, rankdir="TB") + ) try: graph.write_png(file_name) except Exception as e: - print('Error when writing graph to image {}'.format(e)) + print(f'Error when writing graph to image {e}') diff --git a/lib/utils/net.py b/lib/utils/net.py index eaf4827da..c0d9fddc3 100644 --- a/lib/utils/net.py +++ b/lib/utils/net.py @@ -54,7 +54,7 @@ def initialize_gpu_from_weights_file(model, weights_file, gpu_id=0): automatically map logical GPU ids (starting from 0) to the physical GPUs specified in CUDA_VISIBLE_DEVICES. """ - logger.info('Loading weights from: {}'.format(weights_file)) + logger.info(f'Loading weights from: {weights_file}') ws_blobs = workspace.Blobs() with open(weights_file, 'r') as f: src_blobs = pickle.load(f) @@ -86,7 +86,7 @@ def initialize_gpu_from_weights_file(model, weights_file, gpu_id=0): logger.info('{:s} not found'.format(src_name)) continue dst_name = core.ScopedName(unscoped_param_name) - has_momentum = src_name + '_momentum' in src_blobs + has_momentum = f'{src_name}_momentum' in src_blobs has_momentum_str = ' [+ momentum]' if has_momentum else '' logger.debug( '{:s}{:} loaded from weights file into {:s}: {}'.format( @@ -98,20 +98,21 @@ def initialize_gpu_from_weights_file(model, weights_file, gpu_id=0): # If the blob is already in the workspace, make sure that it # matches the shape of the loaded blob ws_blob = workspace.FetchBlob(dst_name) - assert ws_blob.shape == src_blobs[src_name].shape, \ - ('Workspace blob {} with shape {} does not match ' - 'weights file shape {}').format( - src_name, - ws_blob.shape, - src_blobs[src_name].shape) + assert ( + ws_blob.shape == src_blobs[src_name].shape + ), f'Workspace blob {src_name} with shape {ws_blob.shape} does not match weights file shape {src_blobs[src_name].shape}' + workspace.FeedBlob( dst_name, src_blobs[src_name].astype(np.float32, copy=False)) if has_momentum: workspace.FeedBlob( - dst_name + '_momentum', - src_blobs[src_name + '_momentum'].astype( - np.float32, copy=False)) + f'{dst_name}_momentum', + src_blobs[f'{src_name}_momentum'].astype( + np.float32, copy=False + ), + ) + # We preserve blobs that are in the weights file but not used by the current # model. We load these into CPU memory under the '__preserve__/' namescope. @@ -136,8 +137,9 @@ def save_model_to_weights_file(weights_file, model): 'conv1_w'). """ logger.info( - 'Saving parameters and momentum to {}'.format( - os.path.abspath(weights_file))) + f'Saving parameters and momentum to {os.path.abspath(weights_file)}' + ) + blobs = {} # Save all parameters for param in model.params: @@ -148,7 +150,7 @@ def save_model_to_weights_file(weights_file, model): blobs[unscoped_name] = workspace.FetchBlob(scoped_name) # Save momentum for param in model.TrainableParams(): - scoped_name = str(param) + '_momentum' + scoped_name = f'{str(param)}_momentum' unscoped_name = c2_utils.UnscopeName(scoped_name) if unscoped_name not in blobs: logger.debug(' {:s} -> {:s}'.format(scoped_name, unscoped_name)) @@ -180,11 +182,11 @@ def _do_broadcast(all_blobs): 'running single-GPU inference with NUM_GPUS > 1.') blobs_per_gpu = int(len(all_blobs) / cfg.NUM_GPUS) for i in range(blobs_per_gpu): - blobs = [p for p in all_blobs[i::blobs_per_gpu]] + blobs = list(all_blobs[i::blobs_per_gpu]) data = workspace.FetchBlob(blobs[0]) - logger.debug('Broadcasting {} to'.format(str(blobs[0]))) + logger.debug(f'Broadcasting {str(blobs[0])} to') for i, p in enumerate(blobs[1:]): - logger.debug(' |-> {}'.format(str(p))) + logger.debug(f' |-> {str(p)}') with c2_utils.CudaScope(i + 1): workspace.FeedBlob(p, data) @@ -194,10 +196,10 @@ def _do_broadcast(all_blobs): def sum_multi_gpu_blob(blob_name): """Return the sum of a scalar blob held on multiple GPUs.""" - val = 0 - for i in range(cfg.NUM_GPUS): - val += float(workspace.FetchBlob('gpu_{}/{}'.format(i, blob_name))) - return val + return sum( + float(workspace.FetchBlob(f'gpu_{i}/{blob_name}')) + for i in range(cfg.NUM_GPUS) + ) def average_multi_gpu_blob(blob_name): @@ -207,7 +209,7 @@ def average_multi_gpu_blob(blob_name): def print_net(model, namescope='gpu_0'): """Print the model network.""" - logger.info('Printing model: {}'.format(model.net.Name())) + logger.info(f'Printing model: {model.net.Name()}') op_list = model.net.Proto().op for op in op_list: input_name = op.input @@ -219,29 +221,33 @@ def print_net(model, namescope='gpu_0'): if namescope is None or output_name.startswith(namescope): # Only print the forward pass network - if output_name.find('grad') >= 0 or output_name.find('__m') >= 0: + if 'grad' in output_name or '__m' in output_name: continue output_shape = workspace.FetchBlob(output_name).shape first_blob = True - op_label = op_type + (op_name if op_name == '' else ':' + op_name) - suffix = ' ------- (op: {})'.format(op_label) + op_label = op_type + (op_name if op_name == '' else f':{op_name}') + suffix = f' ------- (op: {op_label})' for j in range(len(input_name)): if input_name[j] in model.params: continue input_blob = workspace.FetchBlob(input_name[j]) if isinstance(input_blob, np.ndarray): input_shape = input_blob.shape - logger.info('{:28s}: {:20s} => {:28s}: {:20s}{}'.format( - c2_utils.UnscopeName(str(input_name[j])), - '{}'.format(input_shape), - c2_utils.UnscopeName(str(output_name)), - '{}'.format(output_shape), - suffix)) + logger.info( + '{:28s}: {:20s} => {:28s}: {:20s}{}'.format( + c2_utils.UnscopeName(str(input_name[j])), + f'{input_shape}', + c2_utils.UnscopeName(output_name), + f'{output_shape}', + suffix, + ) + ) + if first_blob: first_blob = False suffix = ' ------|' - logger.info('End of model: {}'.format(model.net.Name())) + logger.info(f'End of model: {model.net.Name()}') def configure_bbox_reg_weights(model, saved_cfg): diff --git a/lib/utils/segms.py b/lib/utils/segms.py index 9b0804dd7..f7f40d9bf 100644 --- a/lib/utils/segms.py +++ b/lib/utils/segms.py @@ -36,7 +36,7 @@ def flip_segms(segms, height, width): """Left/right flip each mask in a list of masks.""" def _flip_poly(poly, width): flipped_poly = np.array(poly) - flipped_poly[0::2] = width - np.array(poly[0::2]) - 1 + flipped_poly[0::2] = width - np.array(poly[::2]) - 1 return flipped_poly.tolist() def _flip_rle(rle, height, width): @@ -105,7 +105,7 @@ def polys_to_mask_wrt_box(polygons, box, M): polygons_norm = [] for poly in polygons: p = np.array(poly, dtype=np.float32) - p[0::2] = (p[0::2] - box[0]) * M / w + p[0::2] = (p[::2] - box[0]) * M / w p[1::2] = (p[1::2] - box[1]) * M / h polygons_norm.append(p) @@ -188,7 +188,7 @@ def rle_mask_voting( soft_mask = np.sum(masks_to_vote, axis=0) mask = np.array(soft_mask > 1e-5, dtype=np.uint8) else: - raise NotImplementedError('Method {} is unknown'.format(method)) + raise NotImplementedError(f'Method {method} is unknown') rle = mask_util.encode(np.array(mask[:, :, np.newaxis], order='F'))[0] top_segms_out.append(rle) @@ -224,7 +224,7 @@ def rle_mask_nms(masks, dets, thresh, mode='IOU'): all_crowds = [True] * len(masks) ious = mask_util.iou(masks, masks, all_crowds) else: - raise NotImplementedError('Mode {} is unknown'.format(mode)) + raise NotImplementedError(f'Mode {mode} is unknown') scores = dets[:, 4] order = np.argsort(-scores) diff --git a/lib/utils/subprocess.py b/lib/utils/subprocess.py index db3fb7fa9..02309497c 100644 --- a/lib/utils/subprocess.py +++ b/lib/utils/subprocess.py @@ -43,15 +43,13 @@ def process_in_parallel(tag, total_range_size, binary, output_dir): """ # Snapshot the current cfg state in order to pass to the inference # subprocesses - cfg_file = os.path.join(output_dir, '{}_range_config.yaml'.format(tag)) + cfg_file = os.path.join(output_dir, f'{tag}_range_config.yaml') with open(cfg_file, 'w') as f: yaml.dump(cfg, stream=f) subprocess_env = os.environ.copy() processes = [] subinds = np.array_split(range(total_range_size), cfg.NUM_GPUS) - # Determine GPUs to use - cuda_visible_devices = os.environ.get('CUDA_VISIBLE_DEVICES') - if cuda_visible_devices: + if cuda_visible_devices := os.environ.get('CUDA_VISIBLE_DEVICES'): gpu_inds = map(int, cuda_visible_devices.split(',')) assert -1 not in gpu_inds, \ 'Hiding GPU indices using the \'-1\' index is not supported' @@ -69,13 +67,11 @@ def process_in_parallel(tag, total_range_size, binary, output_dir): end=int(end), cfg_file=shlex_quote(cfg_file) ) - logger.info('{} range command {}: {}'.format(tag, i, cmd)) + logger.info(f'{tag} range command {i}: {cmd}') if i == 0: subprocess_stdout = subprocess.PIPE else: - filename = os.path.join( - output_dir, '%s_range_%s_%s.stdout' % (tag, start, end) - ) + filename = os.path.join(output_dir, f'{tag}_range_{start}_{end}.stdout') subprocess_stdout = open(filename, 'w') # NOQA (close below) p = subprocess.Popen( cmd, @@ -92,9 +88,7 @@ def process_in_parallel(tag, total_range_size, binary, output_dir): log_subprocess_output(i, p, output_dir, tag, start, end) if isinstance(subprocess_stdout, file): # NOQA (Python 2 for now) subprocess_stdout.close() - range_file = os.path.join( - output_dir, '%s_range_%s_%s.pkl' % (tag, start, end) - ) + range_file = os.path.join(output_dir, f'{tag}_range_{start}_{end}.pkl') range_data = pickle.load(open(range_file)) outputs.append(range_data) return outputs @@ -106,13 +100,9 @@ def log_subprocess_output(i, p, output_dir, tag, start, end): other subprocesses is buffered and then printed all at once (in order) when subprocesses finish. """ - outfile = os.path.join( - output_dir, '%s_range_%s_%s.stdout' % (tag, start, end) - ) + outfile = os.path.join(output_dir, f'{tag}_range_{start}_{end}.stdout') logger.info('# ' + '-' * 76 + ' #') - logger.info( - 'stdout of subprocess %s with range [%s, %s]' % (i, start + 1, end) - ) + logger.info(f'stdout of subprocess {i} with range [{start + 1}, {end}]') logger.info('# ' + '-' * 76 + ' #') if i == 0: # Stream the piped stdout from the first subprocess in realtime @@ -127,4 +117,4 @@ def log_subprocess_output(i, p, output_dir, tag, start, end): ret = p.wait() with open(outfile, 'r') as f: print(''.join(f.readlines())) - assert ret == 0, 'Range subprocess failed (exit code: {})'.format(ret) + assert ret == 0, f'Range subprocess failed (exit code: {ret})' diff --git a/lib/utils/timer.py b/lib/utils/timer.py index 69a20dbde..a02b0f808 100644 --- a/lib/utils/timer.py +++ b/lib/utils/timer.py @@ -47,10 +47,7 @@ def toc(self, average=True): self.total_time += self.diff self.calls += 1 self.average_time = self.total_time / self.calls - if average: - return self.average_time - else: - return self.diff + return self.average_time if average else self.diff def reset(self): self.total_time = 0. diff --git a/lib/utils/vis.py b/lib/utils/vis.py index 750a8cef3..a7f9fbaf7 100644 --- a/lib/utils/vis.py +++ b/lib/utils/vis.py @@ -45,7 +45,7 @@ def kp_connections(keypoints): - kp_lines = [ + return [ [keypoints.index('left_eye'), keypoints.index('right_eye')], [keypoints.index('left_eye'), keypoints.index('nose')], [keypoints.index('right_eye'), keypoints.index('nose')], @@ -62,7 +62,6 @@ def kp_connections(keypoints): [keypoints.index('right_shoulder'), keypoints.index('left_shoulder')], [keypoints.index('right_hip'), keypoints.index('left_hip')], ] - return kp_lines def convert_from_cls_format(cls_boxes, cls_segms, cls_keyps): @@ -70,10 +69,7 @@ def convert_from_cls_format(cls_boxes, cls_segms, cls_keyps): code. """ box_list = [b for b in cls_boxes if len(b) > 0] - if len(box_list) > 0: - boxes = np.concatenate(box_list) - else: - boxes = None + boxes = np.concatenate(box_list) if box_list else None if cls_segms is not None: segms = [s for slist in cls_segms for s in slist] else: @@ -384,6 +380,6 @@ def vis_one_image( line, color=colors[len(kp_lines) + 1], linewidth=1.0, alpha=0.7) - output_name = os.path.basename(im_name) + '.' + ext - fig.savefig(os.path.join(output_dir, '{}'.format(output_name)), dpi=dpi) + output_name = f'{os.path.basename(im_name)}.{ext}' + fig.savefig(os.path.join(output_dir, f'{output_name}'), dpi=dpi) plt.close('all') diff --git a/tests/data_loader_benchmark.py b/tests/data_loader_benchmark.py index 775f14e2f..0a7a92ec2 100644 --- a/tests/data_loader_benchmark.py +++ b/tests/data_loader_benchmark.py @@ -83,8 +83,7 @@ def parse_args(): if len(sys.argv) == 1: parser.print_help() sys.exit(1) - args = parser.parse_args() - return args + return parser.parse_args() def loader_loop(roi_data_loader): @@ -114,13 +113,13 @@ def main(opts): net.type = 'dag' all_blobs = [] for gpu_id in range(cfg.NUM_GPUS): - with core.NameScope('gpu_{}'.format(gpu_id)): + with core.NameScope(f'gpu_{gpu_id}'): with core.DeviceScope(muji.OnGPU(gpu_id)): for blob_name in blob_names: blob = core.ScopedName(blob_name) all_blobs.append(blob) workspace.CreateBlob(blob) - logger.info('Creating blob: {}'.format(blob)) + logger.info(f'Creating blob: {blob}') net.DequeueBlobs( roi_data_loader._blobs_queue_name, blob_names) logger.info("Protobuf:\n" + str(net.Proto())) diff --git a/tests/test_loader.py b/tests/test_loader.py index a30e30e86..bf1db8b74 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -31,8 +31,7 @@ def get_roidb_blobs(roidb): - blobs = {} - blobs['data'] = np.stack([entry['data'] for entry in roidb]) + blobs = {'data': np.stack([entry['data'] for entry in roidb])} return blobs, True @@ -42,7 +41,7 @@ def get_net(data_loader, name): net = core.Net(name) net.type = 'dag' for gpu_id in range(cfg.NUM_GPUS): - with core.NameScope('gpu_{}'.format(gpu_id)): + with core.NameScope(f'gpu_{gpu_id}'): with core.DeviceScope(muji.OnGPU(gpu_id)): for blob_name in blob_names: blob = core.ScopedName(blob_name) @@ -55,10 +54,7 @@ def get_net(data_loader, name): def get_roidb_sample_data(sample_data): - roidb = [] - for _ in range(np.random.randint(4, 10)): - roidb.append({'data': sample_data}) - return roidb + return [{'data': sample_data} for _ in range(np.random.randint(4, 10))] def create_loader_and_network(sample_data, name): @@ -73,11 +69,10 @@ def create_loader_and_network(sample_data, name): def run_net(net): workspace.RunNetOnce(net) gpu_dev = core.DeviceOption(caffe2_pb2.CUDA, 0) - name_scope = 'gpu_{}'.format(0) + name_scope = 'gpu_0' with core.NameScope(name_scope): with core.DeviceScope(gpu_dev): - data = workspace.FetchBlob(core.ScopedName('data')) - return data + return workspace.FetchBlob(core.ScopedName('data')) class TestRoIDataLoader(unittest.TestCase): diff --git a/tests/test_zero_even_op.py b/tests/test_zero_even_op.py index 65aa1c082..12c9ef9e2 100644 --- a/tests/test_zero_even_op.py +++ b/tests/test_zero_even_op.py @@ -19,16 +19,14 @@ def _run_zero_even_op(self, X): op = core.CreateOperator('ZeroEven', ['X'], ['Y']) workspace.FeedBlob('X', X) workspace.RunOperatorOnce(op) - Y = workspace.FetchBlob('Y') - return Y + return workspace.FetchBlob('Y') def _run_zero_even_op_gpu(self, X): with core.DeviceScope(core.DeviceOption(caffe2_pb2.CUDA, 0)): op = core.CreateOperator('ZeroEven', ['X'], ['Y']) workspace.FeedBlob('X', X) workspace.RunOperatorOnce(op) - Y = workspace.FetchBlob('Y') - return Y + return workspace.FetchBlob('Y') def test_throws_on_non_1D_arrays(self): X = np.zeros((2, 2), dtype=np.float32) @@ -45,7 +43,7 @@ def test_sets_vals_at_even_inds_to_zero(self): X = np.array([0, 1, 2, 3, 4], dtype=np.float32) Y_exp = np.array([0, 1, 0, 3, 0], dtype=np.float32) Y_act = self._run_zero_even_op(X) - np.testing.assert_allclose(Y_act[0::2], Y_exp[0::2]) + np.testing.assert_allclose(Y_act[::2], Y_exp[::2]) def test_preserves_vals_at_odd_inds(self): X = np.array([0, 1, 2, 3, 4], dtype=np.float32) @@ -82,7 +80,7 @@ def test_gpu_sets_vals_at_even_inds_to_zero(self): X = np.array([0, 1, 2, 3, 4], dtype=np.float32) Y_exp = np.array([0, 1, 0, 3, 0], dtype=np.float32) Y_act = self._run_zero_even_op_gpu(X) - np.testing.assert_allclose(Y_act[0::2], Y_exp[0::2]) + np.testing.assert_allclose(Y_act[::2], Y_exp[::2]) def test_gpu_preserves_vals_at_odd_inds(self): X = np.array([0, 1, 2, 3, 4], dtype=np.float32) diff --git a/tools/convert_pkl_to_pb.py b/tools/convert_pkl_to_pb.py index 9e113eafb..8a6f353ca 100644 --- a/tools/convert_pkl_to_pb.py +++ b/tools/convert_pkl_to_pb.py @@ -119,7 +119,7 @@ def unscope_name(name): def reset_names(names): - for i in range(0, len(names)): + for i in range(len(names)): names[i] = unscope_name(names[i]) @@ -136,11 +136,11 @@ def convert_gen_proposals( spatial_scale = mutils.get_op_arg_valf(op, "spatial_scale", None) assert spatial_scale is not None - inputs = [x for x in op.input] + inputs = list(op.input) anchor_name = "anchor" inputs.append(anchor_name) blobs[anchor_name] = get_anchors(spatial_scale) - print('anchors {}'.format(blobs[anchor_name])) + print(f'anchors {blobs[anchor_name]}') ret = core.CreateOperator( "GenerateProposals", @@ -158,11 +158,11 @@ def convert_gen_proposals( def get_anchors(spatial_scale): - anchors = generate_anchors.generate_anchors( - stride=1. / spatial_scale, + return generate_anchors.generate_anchors( + stride=1.0 / spatial_scale, sizes=cfg.RPN.SIZES, - aspect_ratios=cfg.RPN.ASPECT_RATIOS).astype(np.float32) - return anchors + aspect_ratios=cfg.RPN.ASPECT_RATIOS, + ).astype(np.float32) def reset_blob_names(blobs): @@ -197,7 +197,7 @@ def convert_gen_proposal(op_in): @op_filter(input_has='rois') def convert_rpn_rois(op): - for j in range(0, len(op.input)): + for j in range(len(op.input)): if op.input[j] == 'rois': print('Converting op {} input name: rois -> rpn_rois:\n{}'.format( op.type, op)) @@ -221,9 +221,6 @@ def convert_remove_op(op): def add_bbox_ops(args, net, blobs): - new_ops = [] - new_external_outputs = [] - # Operators for bboxes op_box = core.CreateOperator( "BBoxTransform", @@ -233,8 +230,7 @@ def add_bbox_ops(args, net, blobs): apply_scale=False, correct_transform_coords=True, ) - new_ops.extend([op_box]) - + new_ops = [op_box] blob_prob = 'cls_prob' blob_box = 'pred_bbox' op_nms = core.CreateOperator( @@ -251,8 +247,7 @@ def add_bbox_ops(args, net, blobs): ] ) new_ops.extend([op_nms]) - new_external_outputs.extend(['score_nms', 'bbox_nms', 'class_nms']) - + new_external_outputs = ['score_nms', 'bbox_nms', 'class_nms'] net.Proto().op.extend(new_ops) net.Proto().external_output.extend(new_external_outputs) @@ -312,21 +307,21 @@ def _save_image_graphs(args, all_net, all_init_net): mutils.save_graph( all_net.Proto(), os.path.join(args.out_dir, "model_def.png"), op_only=False) - print('Model def image saved to {}.'.format(args.out_dir)) + print(f'Model def image saved to {args.out_dir}.') def _save_models(all_net, all_init_net, args): - print('Writing converted model to {}...'.format(args.out_dir)) + print(f'Writing converted model to {args.out_dir}...') fname = "model" if not os.path.exists(args.out_dir): os.makedirs(args.out_dir) - with open(os.path.join(args.out_dir, fname + '.pb'), 'w') as f: + with open(os.path.join(args.out_dir, f'{fname}.pb'), 'w') as f: f.write(all_net.Proto().SerializeToString()) - with open(os.path.join(args.out_dir, fname + '.pbtxt'), 'w') as f: + with open(os.path.join(args.out_dir, f'{fname}.pbtxt'), 'w') as f: f.write(str(all_net.Proto())) - with open(os.path.join(args.out_dir, fname + '_init.pb'), 'w') as f: + with open(os.path.join(args.out_dir, f'{fname}_init.pb'), 'w') as f: f.write(all_init_net.Proto().SerializeToString()) _save_image_graphs(args, all_net, all_init_net) @@ -343,11 +338,7 @@ def _get_result_blobs(check_blobs): ret = {} for x in check_blobs: sn = core.ScopedName(x) - if workspace.HasBlob(sn): - ret[x] = workspace.FetchBlob(sn) - else: - ret[x] = None - + ret[x] = workspace.FetchBlob(sn) if workspace.HasBlob(sn) else None return ret @@ -411,8 +402,8 @@ def _prepare_blobs( im -= pixel_means im_shape = im.shape - im_size_min = np.min(im_shape[0:2]) - im_size_max = np.max(im_shape[0:2]) + im_size_min = np.min(im_shape[:2]) + im_size_max = np.max(im_shape[:2]) im_scale = float(target_size) / float(im_size_min) if np.round(im_scale * im_size_max) > max_size: im_scale = float(max_size) / float(im_size_max) @@ -424,13 +415,12 @@ def _prepare_blobs( channel_swap = (0, 3, 1, 2) # swap channel to (k, c, h, w) blob = blob.transpose(channel_swap) - blobs = {} - blobs['data'] = blob - blobs['im_info'] = np.array( - [[blob.shape[2], blob.shape[3], im_scale]], - dtype=np.float32 - ) - return blobs + return { + 'data': blob, + 'im_info': np.array( + [[blob.shape[2], blob.shape[3], im_scale]], dtype=np.float32 + ), + } def run_model_pb(args, net, init_net, im, check_blobs): @@ -447,9 +437,7 @@ def run_model_pb(args, net, init_net, im, check_blobs): cfg.PIXEL_MEANS, cfg.TEST.SCALES[0], cfg.TEST.MAX_SIZE ) - gpu_blobs = [] - if args.device == 'gpu': - gpu_blobs = ['data'] + gpu_blobs = ['data'] if args.device == 'gpu' else [] for k, v in input_blobs.items(): workspace.FeedBlob( core.ScopedName(k), @@ -481,9 +469,7 @@ def run_model_pb(args, net, init_net, im, check_blobs): workspace.FeedBlob('result_boxes', boxes) workspace.FeedBlob('result_classids', classids) - ret = _get_result_blobs(check_blobs) - - return ret + return _get_result_blobs(check_blobs) def verify_model(args, model_pb, test_img_file): @@ -559,7 +545,7 @@ def main(): [net, init_net] = convert_model_gpu(args, net, init_net) net.Proto().name = args.net_name - init_net.Proto().name = args.net_name + "_init" + init_net.Proto().name = f"{args.net_name}_init" if args.test_img is not None: verify_model(args, [net, init_net], args.test_img) diff --git a/tools/convert_selective_search.py b/tools/convert_selective_search.py index 9d19b1e70..59d8fab13 100644 --- a/tools/convert_selective_search.py +++ b/tools/convert_selective_search.py @@ -19,6 +19,7 @@ file format. """ + from __future__ import absolute_import from __future__ import division from __future__ import print_function @@ -46,7 +47,7 @@ ids = [] for i in range(raw_data.shape[0]): if i % 1000 == 0: - print('{}/{}'.format(i + 1, len(roidb))) + print(f'{i + 1}/{len(roidb)}') # selective search boxes are 1-indexed and (y1, x1, y2, x2) i_boxes = raw_data[i][:, (1, 0, 3, 2)] - 1 boxes.append(i_boxes.astype(np.float32)) diff --git a/tools/generate_testdev_from_test.py b/tools/generate_testdev_from_test.py index f48590972..530659f9f 100644 --- a/tools/generate_testdev_from_test.py +++ b/tools/generate_testdev_from_test.py @@ -49,12 +49,11 @@ def parse_args(): if len(sys.argv) == 1: parser.print_help() sys.exit(1) - args = parser.parse_args() - return args + return parser.parse_args() def convert(json_file, output_dir): - print('Reading: {}'.format(json_file)) + print(f'Reading: {json_file}') with open(json_file, 'r') as fid: dt = json.load(fid) print('done!') @@ -64,14 +63,14 @@ def convert(json_file, output_dir): info_test = json.load(fid) image_test = info_test['images'] image_test_id = [i['id'] for i in image_test] - print('{} has {} images'.format(test_image_info, len(image_test_id))) + print(f'{test_image_info} has {len(image_test_id)} images') test_dev_image_info = DATASETS['coco_2017_test-dev'][ANN_FN] with open(test_dev_image_info, 'r') as fid: info_testdev = json.load(fid) image_testdev = info_testdev['images'] image_testdev_id = [i['id'] for i in image_testdev] - print('{} has {} images'.format(test_dev_image_info, len(image_testdev_id))) + print(f'{test_dev_image_info} has {len(image_testdev_id)} images') dt_testdev = [] print('Filtering test-dev from test...') @@ -79,17 +78,17 @@ def convert(json_file, output_dir): t.tic() for i in range(len(dt)): if i % 1000 == 0: - print('{}/{}'.format(i, len(dt))) + print(f'{i}/{len(dt)}') if dt[i]['image_id'] in image_testdev_id: dt_testdev.append(dt[i]) print('Done filtering ({:2}s)!'.format(t.toc())) filename, file_extension = os.path.splitext(os.path.basename(json_file)) - filename = filename + '_test-dev' + filename = f'{filename}_test-dev' filename = os.path.join(output_dir, filename + file_extension) with open(filename, 'w') as fid: info_test = json.dump(dt_testdev, fid) - print('Done writing: {}!'.format(filename)) + print(f'Done writing: {filename}!') if __name__ == '__main__': diff --git a/tools/infer_simple.py b/tools/infer_simple.py index 8b17dd0fd..9ab21b67c 100644 --- a/tools/infer_simple.py +++ b/tools/infer_simple.py @@ -100,15 +100,13 @@ def main(args): dummy_coco_dataset = dummy_datasets.get_coco_dataset() if os.path.isdir(args.im_or_folder): - im_list = glob.iglob(args.im_or_folder + '/*.' + args.image_ext) + im_list = glob.iglob(f'{args.im_or_folder}/*.{args.image_ext}') else: im_list = [args.im_or_folder] for i, im_name in enumerate(im_list): - out_name = os.path.join( - args.output_dir, '{}'.format(os.path.basename(im_name) + '.pdf') - ) - logger.info('Processing {} -> {}'.format(im_name, out_name)) + out_name = os.path.join(args.output_dir, f'{os.path.basename(im_name)}.pdf') + logger.info(f'Processing {im_name} -> {out_name}') im = cv2.imread(im_name) timers = defaultdict(Timer) t = time.time() diff --git a/tools/pickle_caffe_blobs.py b/tools/pickle_caffe_blobs.py index af6bf2bae..2dff40086 100644 --- a/tools/pickle_caffe_blobs.py +++ b/tools/pickle_caffe_blobs.py @@ -68,8 +68,7 @@ def parse_args(): parser.print_help() sys.exit(1) - args = parser.parse_args() - return args + return parser.parse_args() def normalize_resnet_name(name): @@ -79,12 +78,14 @@ def normalize_resnet_name(name): # res2a_branch1 -> res2_0_branch1 chunk = name[len('res'):name.find('_')] name = ( - 'res' + chunk[0] + '_' + str( - int(chunk[2:]) if len(chunk) > 2 # e.g., "b1" -> 1 + f'res{chunk[0]}_' + + str( + int(chunk[2:]) + if len(chunk) > 2 # e.g., "b1" -> 1 else ord(chunk[1]) - ord('a') - ) + # e.g., "a" -> 0 - name[name.find('_'):] - ) + ) + ) + name[name.find('_') :] + return name @@ -169,7 +170,7 @@ def remove_layers_without_parameters(caffenet, caffenet_weights): found = True break if not found and name[-len('_split'):] != '_split': - print('Warning: layer {} not found in caffenet'.format(name)) + print(f'Warning: layer {name} not found in caffenet') caffenet_weights.layer.pop(i) diff --git a/tools/reval.py b/tools/reval.py index 8be9d2dab..589534859 100755 --- a/tools/reval.py +++ b/tools/reval.py @@ -79,8 +79,7 @@ def parse_args(): parser.print_help() sys.exit(1) - args = parser.parse_args() - return args + return parser.parse_args() def do_reval(dataset_name, output_dir, args): diff --git a/tools/test_net.py b/tools/test_net.py index 8ee844ce4..19d46a295 100755 --- a/tools/test_net.py +++ b/tools/test_net.py @@ -120,7 +120,7 @@ def main(ind_range=None, multi_gpu_testing=False): if cfg.TEST.PRECOMPUTED_PROPOSALS: cfg.TEST.PROPOSAL_FILE = cfg.TEST.PROPOSAL_FILES[i] results = parent_func(multi_gpu=multi_gpu_testing) - all_results.update(results) + all_results |= results task_evaluation.check_expected_results( all_results, @@ -151,7 +151,7 @@ def main(ind_range=None, multi_gpu_testing=False): logger.info(pprint.pformat(cfg)) while not os.path.exists(cfg.TEST.WEIGHTS) and args.wait: - logger.info('Waiting for \'{}\' to exist...'.format(cfg.TEST.WEIGHTS)) + logger.info(f"Waiting for '{cfg.TEST.WEIGHTS}' to exist...") time.sleep(10) main(ind_range=args.range, multi_gpu_testing=args.multi_gpu_testing) diff --git a/tools/train_net.py b/tools/train_net.py index c74cf3e84..c10ca5a15 100755 --- a/tools/train_net.py +++ b/tools/train_net.py @@ -145,9 +145,7 @@ def train_model(): training_stats.LogIterStats(cur_iter, lr) if (cur_iter + 1) % CHECKPOINT_PERIOD == 0 and cur_iter > start_iter: - checkpoints[cur_iter] = os.path.join( - output_dir, 'model_iter{}.pkl'.format(cur_iter) - ) + checkpoints[cur_iter] = os.path.join(output_dir, f'model_iter{cur_iter}.pkl') nu.save_model_to_weights_file(checkpoints[cur_iter], model) if cur_iter == start_iter + training_stats.LOG_PERIOD: @@ -198,11 +196,11 @@ def create_model(): # Override the initialization weights with the found checkpoint cfg.TRAIN.WEIGHTS = os.path.join(output_dir, resume_weights_file) logger.info( - '========> Resuming from checkpoint {} at start iter {}'. - format(cfg.TRAIN.WEIGHTS, start_iter) + f'========> Resuming from checkpoint {cfg.TRAIN.WEIGHTS} at start iter {start_iter}' ) - logger.info('Building model: {}'.format(cfg.MODEL.TYPE)) + + logger.info(f'Building model: {cfg.MODEL.TYPE}') model = model_builder.create(cfg.MODEL.TYPE, train=True) if cfg.MEMONGER: optimize_memory(model) @@ -214,7 +212,7 @@ def create_model(): def optimize_memory(model): """Save GPU memory through blob sharing.""" for device in range(cfg.NUM_GPUS): - namescope = 'gpu_{}/'.format(device) + namescope = f'gpu_{device}/' losses = [namescope + l for l in model.losses] model.net._net = memonger.share_grad_blobs( model.net, @@ -250,7 +248,7 @@ def setup_model_for_training(model, output_dir): def add_model_training_inputs(model): """Load the training dataset and attach the training inputs to the model.""" logger = logging.getLogger(__name__) - logger.info('Loading dataset: {}'.format(cfg.TRAIN.DATASETS)) + logger.info(f'Loading dataset: {cfg.TRAIN.DATASETS}') roidb = combined_roidb_for_training( cfg.TRAIN.DATASETS, cfg.TRAIN.PROPOSAL_FILES ) diff --git a/tools/visualize_results.py b/tools/visualize_results.py index edf449d7a..fd4114431 100644 --- a/tools/visualize_results.py +++ b/tools/visualize_results.py @@ -76,8 +76,7 @@ def parse_args(): if len(sys.argv) == 1: parser.print_help() sys.exit(1) - args = parser.parse_args() - return args + return parser.parse_args() def vis(dataset, detections_pkl, thresh, output_dir, limit=0): @@ -99,10 +98,7 @@ def vis(dataset, detections_pkl, thresh, output_dir, limit=0): all_keyps = None def id_or_index(ix, val): - if len(val) == 0: - return val - else: - return val[ix] + return val if len(val) == 0 else val[ix] for ix, entry in enumerate(roidb): if limit > 0 and ix >= limit: